mirror of
https://github.com/AlexandreRouma/dsp2.git
synced 2026-04-18 14:52:43 +00:00
progress
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
build/
|
build/
|
||||||
.old/
|
.old/
|
||||||
|
unfinished/
|
||||||
@@ -9,7 +9,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE "/")
|
|||||||
|
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE /std:c++20)
|
target_compile_options(${PROJECT_NAME} PRIVATE /std:c++20)
|
||||||
|
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# Lib path
|
# Lib path
|
||||||
target_link_directories(${PROJECT_NAME} PUBLIC "C:/Program Files/PothosSDR/lib/")
|
target_link_directories(${PROJECT_NAME} PUBLIC "C:/Program Files/PothosSDR/lib/")
|
||||||
|
|||||||
112
dsp/block.cpp
112
dsp/block.cpp
@@ -1,112 +0,0 @@
|
|||||||
#include "block.h"
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace dsp {
|
|
||||||
Block::Block() {}
|
|
||||||
|
|
||||||
Block::~Block() {
|
|
||||||
// Stop the worker
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::start() {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Do nothing if the block is already running
|
|
||||||
if (_running) { return; }
|
|
||||||
|
|
||||||
// Mark as running
|
|
||||||
_running = true;
|
|
||||||
|
|
||||||
// Start the worker thread
|
|
||||||
workerThread = std::thread(&Block::worker, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::stop() {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Do nothing if the block is not running
|
|
||||||
if (!_running) { return; }
|
|
||||||
|
|
||||||
// Set the receive stop flag on all input streams
|
|
||||||
for (const auto& input : inputs) {
|
|
||||||
input->stopReceiver();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the send stop flag on all output streams
|
|
||||||
for (const auto& output : outputs) {
|
|
||||||
output->stopSender();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the thread to exist
|
|
||||||
if (workerThread.joinable()) { workerThread.join(); }
|
|
||||||
|
|
||||||
// Clear the receive stop flag on all input streams
|
|
||||||
for (const auto& input : inputs) {
|
|
||||||
input->clearRecvStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the send stop flag on all output streams
|
|
||||||
for (const auto& output : outputs) {
|
|
||||||
output->clearSendStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as not running
|
|
||||||
_running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Block::running() const {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Return run state
|
|
||||||
return _running;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::registerInput(StopNotifier* input) {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Save to the input list
|
|
||||||
inputs.push_back(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::unregisterInput(StopNotifier* input) {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Find the notifier
|
|
||||||
auto it = std::find(inputs.begin(), inputs.end(), input);
|
|
||||||
if (it == inputs.end()) { return; }
|
|
||||||
|
|
||||||
// Remove it from the list
|
|
||||||
inputs.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::registerOutput(StopNotifier* output) {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Save to the output list
|
|
||||||
outputs.push_back(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::unregisterOutput(StopNotifier* output) {
|
|
||||||
// Acquire worker variables
|
|
||||||
std::lock_guard<std::mutex> lck(workerMtx);
|
|
||||||
|
|
||||||
// Find the notifier
|
|
||||||
auto it = std::find(outputs.begin(), outputs.end(), output);
|
|
||||||
if (it == outputs.end()) { return; }
|
|
||||||
|
|
||||||
// Remove it from the list
|
|
||||||
inputs.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::worker() {
|
|
||||||
// Call the run function repeatedly
|
|
||||||
while (run());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
107
dsp/block.h
107
dsp/block.h
@@ -1,107 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dsp {
|
|
||||||
/**
|
|
||||||
* Interface to be used by any blocking class (stream, mailbox, etc...) to allow cancelling an operation.
|
|
||||||
*/
|
|
||||||
class StopNotifier {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Notify the sending thread that it should stop.
|
|
||||||
*/
|
|
||||||
virtual void stopSender() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the sender stop flag to allow restarting the sender thread.
|
|
||||||
*/
|
|
||||||
virtual void clearSendStop() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the receiving thread that it should stop.
|
|
||||||
*/
|
|
||||||
virtual void stopReceiver() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the receiver stop flag to allow restarting the sender thread.
|
|
||||||
*/
|
|
||||||
virtual void clearRecvStop() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* General DSP block class handling the worker thread start/stop operations.
|
|
||||||
* All input and output stop notifiers (usually streams) of the derived blocks must be registered using the appropriate functions.
|
|
||||||
* This class is thread-safe.
|
|
||||||
*/
|
|
||||||
class Block {
|
|
||||||
public:
|
|
||||||
// Default constructor
|
|
||||||
Block();
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
virtual ~Block();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the block's worker thread.
|
|
||||||
*/
|
|
||||||
void start();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the block's worker thread.
|
|
||||||
*/
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check wether or not the block's worker thread is running.
|
|
||||||
*/
|
|
||||||
bool running() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Register an input stop notifier.
|
|
||||||
* @param input Input stop notifier to register.
|
|
||||||
*/
|
|
||||||
void registerInput(StopNotifier* input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregister an input stop notifier.
|
|
||||||
* @param input Input stop notifier to unregister.
|
|
||||||
*/
|
|
||||||
void unregisterInput(StopNotifier* input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register an output stop notifier.
|
|
||||||
* @param input Output stop notifier to register.
|
|
||||||
*/
|
|
||||||
void registerOutput(StopNotifier* output);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregister an output stop notifier.
|
|
||||||
* @param input Output stop notifier to unregister.
|
|
||||||
*/
|
|
||||||
void unregisterOutput(StopNotifier* output);
|
|
||||||
|
|
||||||
// TODO: Pause/Resume for when inputs/outputs change to avoid needing to restart a thread
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the DSP code.
|
|
||||||
* @return False if the worker thread should stop. True otherwise.
|
|
||||||
*/
|
|
||||||
virtual bool run() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Worker thread function.
|
|
||||||
*/
|
|
||||||
void worker();
|
|
||||||
|
|
||||||
// Worker variables
|
|
||||||
mutable std::mutex workerMtx;
|
|
||||||
std::thread workerThread;
|
|
||||||
std::vector<StopNotifier*> inputs;
|
|
||||||
std::vector<StopNotifier*> outputs;
|
|
||||||
bool _running = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -25,10 +25,10 @@ namespace dsp {
|
|||||||
template <class T>
|
template <class T>
|
||||||
Buffer<T>::Buffer(const Buffer<T>& b) {
|
Buffer<T>::Buffer(const Buffer<T>& b) {
|
||||||
// Allocate buffer
|
// Allocate buffer
|
||||||
realloc(b.count);
|
realloc(b.capacity);
|
||||||
|
|
||||||
// Copy data over
|
// Copy data over
|
||||||
memcpy(buffer, b.buffer, b.count * sizeof(T));
|
memcpy(buffer, b.buffer, b.capacity * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -55,6 +55,9 @@ namespace dsp {
|
|||||||
|
|
||||||
// Copy over the data
|
// Copy over the data
|
||||||
memcpy(buffer, b.buffer, capacity * sizeof(T));
|
memcpy(buffer, b.buffer, capacity * sizeof(T));
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -69,22 +72,26 @@ namespace dsp {
|
|||||||
// Neutralize the original
|
// Neutralize the original
|
||||||
b.buffer = NULL;
|
b.buffer = NULL;
|
||||||
b.capacity = 0;
|
b.capacity = 0;
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void Buffer<T>::realloc(size_t size, ReallocBehavior behavior) {
|
void Buffer<T>::realloc(size_t size, ReallocBehavior behavior) {
|
||||||
// Select the desired behavior
|
// Select the desired behavior
|
||||||
|
T* newbuf;
|
||||||
switch (behavior) {
|
switch (behavior) {
|
||||||
case REALLOC_DISCARD:
|
case REALLOC_DISCARD:
|
||||||
// Free the current buffer
|
// Free the current buffer
|
||||||
volk_free(buffer);
|
volk_free(buffer);
|
||||||
|
|
||||||
// Allocate a new buffer
|
// Allocate a new buffer
|
||||||
buffer = volk_malloc(size * sizeof(T));
|
buffer = (T*)volk_malloc(size * sizeof(T), volk_get_alignment());
|
||||||
break;
|
break;
|
||||||
case REALLOC_KEEP:
|
case REALLOC_KEEP:
|
||||||
// Allocate a new buffer
|
// Allocate a new buffer
|
||||||
T* newbuf = volk_malloc(size * sizeof(T));
|
newbuf = (T*)volk_malloc(size * sizeof(T), volk_get_alignment());
|
||||||
|
|
||||||
// Copy the existing data
|
// Copy the existing data
|
||||||
memcpy(newbuf, buffer, std::min<size_t>(capacity, size));
|
memcpy(newbuf, buffer, std::min<size_t>(capacity, size));
|
||||||
@@ -97,13 +104,15 @@ namespace dsp {
|
|||||||
volk_free(buffer);
|
volk_free(buffer);
|
||||||
|
|
||||||
// Allocate a new buffer
|
// Allocate a new buffer
|
||||||
buffer = volk_malloc(size * sizeof(T));
|
buffer = (T*)volk_malloc(size * sizeof(T), volk_get_alignment());
|
||||||
|
|
||||||
// Zero-out the new buffer
|
// Zero-out the new buffer
|
||||||
memset(buffer, 0, size);
|
memset(buffer, 0, size);
|
||||||
break;
|
break;
|
||||||
case REALLOC_KEEP_AND_ZERO:
|
case REALLOC_KEEP_AND_ZERO:
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the current capacity
|
// Update the current capacity
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ namespace dsp {
|
|||||||
re = b;
|
re = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Define in-place operators
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Real component.
|
* Real component.
|
||||||
*/
|
*/
|
||||||
|
|||||||
5
dsp/constants.h
Normal file
5
dsp/constants.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define DSP_PI ((float)3.141592653589793)
|
||||||
|
#define DSP_SQRT2 ((float)1.414213562373095)
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#include "fm.h"
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
namespace dsp::demod {
|
|
||||||
FM_s::FM_s() {}
|
|
||||||
|
|
||||||
FM_s::FM_s(float deviation, float samplerate) {
|
|
||||||
// Save the settings
|
|
||||||
this->deviation = deviation;
|
|
||||||
this->samplerate = samplerate;
|
|
||||||
|
|
||||||
// Update the normalization factor
|
|
||||||
normFact = samplerate / (2.0f * M_PI * deviation);
|
|
||||||
}
|
|
||||||
|
|
||||||
float FM_s::getDeviation() const {
|
|
||||||
// Acquire the settings mutex
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(settingsMtx);
|
|
||||||
|
|
||||||
// Return the deviation
|
|
||||||
return deviation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FM_s::setDeviation(float deviation) {
|
|
||||||
// Acquire the settings mutex
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(settingsMtx);
|
|
||||||
|
|
||||||
// Update the deviation
|
|
||||||
this->deviation = deviation;
|
|
||||||
|
|
||||||
// Update the normalization factor
|
|
||||||
normFact = samplerate / (2.0f * M_PI * deviation);
|
|
||||||
}
|
|
||||||
|
|
||||||
float FM_s::getSamplerate() const {
|
|
||||||
// Acquire the settings mutex
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(settingsMtx);
|
|
||||||
|
|
||||||
// Return the samplerate
|
|
||||||
return samplerate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FM_s::setSamplerate(float samplerate) {
|
|
||||||
// Acquire the settings mutex
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(settingsMtx);
|
|
||||||
|
|
||||||
// Update the samplerate
|
|
||||||
this->samplerate = samplerate;
|
|
||||||
|
|
||||||
// Update the normalization factor
|
|
||||||
normFact = samplerate / (2.0f * M_PI * deviation);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FM_s::reset() {
|
|
||||||
// Acquire the settings mutex
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(settingsMtx);
|
|
||||||
|
|
||||||
// Set the current phase to zero
|
|
||||||
phase = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FM_s::process(const Complex* in, float* out, int count) {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
// Get the current phase
|
|
||||||
float cphase = in[i].phase();
|
|
||||||
|
|
||||||
// Compute the difference with the last phase
|
|
||||||
// TODO
|
|
||||||
//out[i] = math::normalizePhase(cphase - phase) * normFact;
|
|
||||||
|
|
||||||
// Save the current phase for the next iteration
|
|
||||||
phase = cphase;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
FM::FM() {}
|
|
||||||
|
|
||||||
FM::FM(float deviation, float samplerate) :
|
|
||||||
FM_s(deviation, samplerate),
|
|
||||||
ProcessorAsync(this)
|
|
||||||
{}
|
|
||||||
|
|
||||||
FM::FM(Stream<Complex>* in, float deviation, float samplerate) :
|
|
||||||
FM_s(deviation, samplerate),
|
|
||||||
ProcessorAsync(this, in)
|
|
||||||
{}
|
|
||||||
|
|
||||||
FM::FM(Stream<Complex>* in, Stream<float>* out, float deviation, float samplerate) :
|
|
||||||
FM_s(deviation, samplerate),
|
|
||||||
ProcessorAsync(this, in, out)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
100
dsp/demod/fm.h
100
dsp/demod/fm.h
@@ -1,100 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "../processor.h"
|
|
||||||
#include "../complex.h"
|
|
||||||
|
|
||||||
namespace dsp::demod {
|
|
||||||
/**
|
|
||||||
* FM demodulator (Synchronous).
|
|
||||||
* This class is thread-safe except for `process()`.
|
|
||||||
*/
|
|
||||||
class FM_s : public ProcessorSync<Complex, float> {
|
|
||||||
public:
|
|
||||||
// Default constructor
|
|
||||||
FM_s();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an FM demodulator.
|
|
||||||
* @param deviation Deviation of the FM signal in Hz.
|
|
||||||
* @param samplerate Samplerate of the signal in Hz.
|
|
||||||
*/
|
|
||||||
FM_s(float deviation, float samplerate);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the deviation.
|
|
||||||
* @return Deviation of the FM signal in Hz.
|
|
||||||
*/
|
|
||||||
float getDeviation() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the deviation.
|
|
||||||
* @param deviation Deviation of the FM signal in Hz.
|
|
||||||
*/
|
|
||||||
void setDeviation(float deviation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the samplerate.
|
|
||||||
* @return Samplerate of the signal in Hz.
|
|
||||||
*/
|
|
||||||
float getSamplerate() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the samplerate.
|
|
||||||
* @param deviation Samplerate of the signal in Hz.
|
|
||||||
*/
|
|
||||||
void setSamplerate(float samplerate);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the state of the demodulator.
|
|
||||||
*/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Demodulate a FM-modulated signal. A lock must be acquired using `getLock()` prior to invoking if settings could be set from another thread.
|
|
||||||
* @param in Modulated signal buffer.
|
|
||||||
* @param out Demodulated signal buffer.
|
|
||||||
* @param count Number of samples in the input buffer.
|
|
||||||
* @return Number of samples in the output buffer. Will always be equal to the number of samples in the input buffer.
|
|
||||||
*/
|
|
||||||
int process(const Complex* in, float* out, int count);
|
|
||||||
|
|
||||||
private:
|
|
||||||
float deviation = 0.0f;
|
|
||||||
float samplerate = 0.0f;
|
|
||||||
float normFact = 1.0f;
|
|
||||||
float phase = 0.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FM demodulator.
|
|
||||||
* This class is thread-safe.
|
|
||||||
*/
|
|
||||||
class FM : public FM_s, public ProcessorAsync<Complex, float> {
|
|
||||||
public:
|
|
||||||
// Default constructor
|
|
||||||
FM();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an FM demodulator.
|
|
||||||
* @param deviation Deviation of the FM signal in Hz.
|
|
||||||
* @param samplerate Samplerate of the signal in Hz.
|
|
||||||
*/
|
|
||||||
FM(float deviation, float samplerate);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an FM demodulator.
|
|
||||||
* @param in Modulated signal stream.
|
|
||||||
* @param deviation Deviation of the FM signal in Hz.
|
|
||||||
* @param samplerate Samplerate of the signal in Hz.
|
|
||||||
*/
|
|
||||||
FM(Stream<Complex>* in, float deviation, float samplerate);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an FM demodulator.
|
|
||||||
* @param in Modulated signal stream.
|
|
||||||
* @param out Demodulated signal stream.
|
|
||||||
* @param deviation Deviation of the FM signal in Hz.
|
|
||||||
* @param samplerate Samplerate of the signal in Hz.
|
|
||||||
*/
|
|
||||||
FM(Stream<Complex>* in, Stream<float>* out, float deviation, float samplerate);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "../processor.h"
|
|
||||||
#include "../taps.h"
|
|
||||||
|
|
||||||
namespace dsp::filter {
|
|
||||||
/**
|
|
||||||
* Finite Inpulse Response filter (Synchronous).
|
|
||||||
* This class is thread-safe except for `process()`.
|
|
||||||
*/
|
|
||||||
template <class SAMP_T, class TAPS_T>
|
|
||||||
class FIR_s : public ProcessorSync<SAMP_T, SAMP_T> {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a FIR filter.
|
|
||||||
* @param taps Filter taps.
|
|
||||||
*/
|
|
||||||
FIR_s(const Taps<TAPS_T>& taps);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the filter taps.
|
|
||||||
* @return Filter taps.
|
|
||||||
*/
|
|
||||||
Taps<TAPS_T> getTaps();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the filter taps.
|
|
||||||
* @param taps Filter taps.
|
|
||||||
*/
|
|
||||||
void setTaps(const Taps<TAPS_T>& taps);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the state of the filter.
|
|
||||||
*/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter samples.
|
|
||||||
* @param in Input sample buffer.
|
|
||||||
* @param out Filtered sample buffer.
|
|
||||||
* @param count Number of samples in the input buffer.
|
|
||||||
* @return Number of samples in the output buffer. Will always be equal to the number of samples in the input buffer.
|
|
||||||
*/
|
|
||||||
int process(const SAMP_T* in, SAMP_T* out, int count);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Taps<TAPS_T> taps;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finite Inpulse Response filter.
|
|
||||||
* This class is thread-safe except for `process()`.
|
|
||||||
*/
|
|
||||||
template <class SAMP_T, class TAPS_T>
|
|
||||||
class FIR : public FIR_s<SAMP_T, TAPS_T>, public ProcessorAsync<SAMP_T, SAMP_T> {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a FIR filter.
|
|
||||||
* @param taps Filter taps.
|
|
||||||
*/
|
|
||||||
FIR(const Taps<TAPS_T>& taps);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a FIR filter.
|
|
||||||
* @param in Input samples.
|
|
||||||
* @param taps Filter taps.
|
|
||||||
*/
|
|
||||||
FIR(Stream<SAMP_T>* in, const Taps<TAPS_T>& taps);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a FIR filter.
|
|
||||||
* @param in Input samples.
|
|
||||||
* @param in Filtered samples.
|
|
||||||
* @param taps Filter taps.
|
|
||||||
*/
|
|
||||||
FIR(Stream<SAMP_T>* in, Stream<SAMP_T>* out, const Taps<TAPS_T>& taps);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
164
dsp/processor.h
164
dsp/processor.h
@@ -1,164 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <type_traits>
|
|
||||||
#include "block.h"
|
|
||||||
#include "stream.h"
|
|
||||||
|
|
||||||
namespace dsp {
|
|
||||||
// TODO: Deal with the fact that always locking will be slow in hier blocks...
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class representing a DSP kernel taking one input and one output.
|
|
||||||
* Dervied classes must be thread-safe by locking the provided `settingsMtx` mutex in any function changing settings.
|
|
||||||
*/
|
|
||||||
template <class IN, class OUT>
|
|
||||||
class ProcessorKernel {
|
|
||||||
public:
|
|
||||||
// Destructor
|
|
||||||
virtual ~ProcessorKernel() {}
|
|
||||||
|
|
||||||
// TODO: Copy/Move Constructor/Operator
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquire a lock to the settings of the kernel. Mandatory if settings are changed in a different thread than the processing function.
|
|
||||||
* @return Lock to the settings of the block.
|
|
||||||
*/
|
|
||||||
inline std::lock_guard<std::recursive_mutex> getLock() const {
|
|
||||||
return std::lock_guard<std::recursive_mutex>(settingsMtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process samples. This function is NOT thread-safe.
|
|
||||||
* @param in Input buffer.
|
|
||||||
* @param out Output buffer.
|
|
||||||
* @param count Number of samples in the input buffer.
|
|
||||||
* @return Number of samples in the output buffer.
|
|
||||||
*/
|
|
||||||
virtual int process(const IN* in, OUT* out, int count) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Mutex to be used for kernel settings.
|
|
||||||
*/
|
|
||||||
mutable std::recursive_mutex settingsMtx;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class IN, class OUT>
|
|
||||||
class ProcessorBlock : public Block {
|
|
||||||
public:
|
|
||||||
// Default constructor
|
|
||||||
ProcessorBlock() {
|
|
||||||
// TODO: Maybe something to do to prevent bad shit if started?
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
ProcessorBlock(ProcessorSync<IN, OUT>* proc) {
|
|
||||||
// Save the pointer to the processor
|
|
||||||
this->proc = proc;
|
|
||||||
|
|
||||||
// Set the streams
|
|
||||||
setInput(NULL);
|
|
||||||
setOutput(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
ProcessorBlock(ProcessorSync<IN, OUT>* proc, Stream<IN>* in) {
|
|
||||||
// Save the pointer to the processor
|
|
||||||
this->proc = proc;
|
|
||||||
|
|
||||||
// Set the streams
|
|
||||||
setInput(in);
|
|
||||||
setOutput(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
ProcessorBlock(ProcessorSync<IN, OUT>* proc, Stream<IN>* in, Stream<OUT>* out) {
|
|
||||||
// Save the pointer to the processor
|
|
||||||
this->proc = proc;
|
|
||||||
|
|
||||||
// Set the streams
|
|
||||||
setInput(in);
|
|
||||||
setOutput(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
virtual ~ProcessorBlock() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the input stream.
|
|
||||||
* @param in Input stream.
|
|
||||||
*/
|
|
||||||
void setInput(Stream<IN>* in) {
|
|
||||||
// TODO: Lock
|
|
||||||
|
|
||||||
// If the current input if it already was registered
|
|
||||||
unregisterInput(_in);
|
|
||||||
|
|
||||||
// TODO: Manage allocating and freeing streams
|
|
||||||
|
|
||||||
// Update the input
|
|
||||||
_in = in;
|
|
||||||
|
|
||||||
// Register the new input
|
|
||||||
registerInput(_in);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the output stream.
|
|
||||||
* @param in Output stream.
|
|
||||||
*/
|
|
||||||
void setOutput(Stream<OUT>* out) {
|
|
||||||
// TODO: Lock
|
|
||||||
|
|
||||||
// If the current output if it already was registered
|
|
||||||
unregisterOutput(_out);
|
|
||||||
|
|
||||||
// TODO: Manage allocating and freeing streams
|
|
||||||
|
|
||||||
// Update the output
|
|
||||||
_out = out;
|
|
||||||
|
|
||||||
// Register the new output
|
|
||||||
registerOutput(_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<IN>* in() const {
|
|
||||||
// TODO: Lock
|
|
||||||
return _in;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<OUT>* out() const {
|
|
||||||
// TODO: Lock
|
|
||||||
return _out;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool run() {
|
|
||||||
// Read samples
|
|
||||||
auto bufSet = _in->recv();
|
|
||||||
if (!bufSet.samples) { return true; }
|
|
||||||
|
|
||||||
// Process samples
|
|
||||||
{
|
|
||||||
auto lck = proc->getLock();
|
|
||||||
proc->process(NULL/*TODO*/, NULL/*TODO*/, 0/*TODO*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Write samples
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessorKernel<IN, OUT>* proc;
|
|
||||||
Stream<IN>* _in = NULL;
|
|
||||||
Stream<OUT>* _out = NULL;
|
|
||||||
bool ownInput = false;
|
|
||||||
bool ownOutput = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
28
dsp/stream.h
28
dsp/stream.h
@@ -1,7 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include "block.h"
|
//#include "block.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be used by any blocking class (stream, mailbox, etc...) to allow cancelling an operation.
|
||||||
|
*/
|
||||||
|
class StopNotifier {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Notify the sending thread that it should stop.
|
||||||
|
*/
|
||||||
|
virtual void stopSender() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the sender stop flag to allow restarting the sender thread.
|
||||||
|
*/
|
||||||
|
virtual void clearSendStop() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the receiving thread that it should stop.
|
||||||
|
*/
|
||||||
|
virtual void stopReceiver() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the receiver stop flag to allow restarting the sender thread.
|
||||||
|
*/
|
||||||
|
virtual void clearRecvStop() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ namespace dsp {
|
|||||||
template <class T>
|
template <class T>
|
||||||
Taps<T>::Taps(const T* taps, size_t count) : Buffer<T>(taps, count) {}
|
Taps<T>::Taps(const T* taps, size_t count) : Buffer<T>(taps, count) {}
|
||||||
|
|
||||||
template class Buffer<float>;
|
template class Taps<float>;
|
||||||
template class Buffer<double>;
|
template class Taps<Complex>;
|
||||||
template class Buffer<Complex>;
|
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,68 @@
|
|||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
#include "complex.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
|
Window::Window() {
|
||||||
|
// Define the window with the default error
|
||||||
|
define();
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::Window(const Window& b) {
|
||||||
|
// Copy the definition
|
||||||
|
def = b.def;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::Window(Window&& b) {
|
||||||
|
// Move the definition
|
||||||
|
def = std::move(b.def);
|
||||||
|
}
|
||||||
|
|
||||||
Window::~Window() {}
|
Window::~Window() {}
|
||||||
|
|
||||||
void Window::generate(float* data, size_t len) const {
|
Window& Window::operator=(const Window& b) {
|
||||||
for (size_t i = 0; i < len; i++) {
|
// Copy the definition
|
||||||
|
def = b.def;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window& Window::operator=(Window&& b) {
|
||||||
|
// Move the definition
|
||||||
|
def = std::move(b.def);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::generate(float* data, size_t len) const {
|
||||||
|
// Compute the linear map ratio
|
||||||
|
float ratio = 1.0f / ((float)(len + 1));
|
||||||
|
|
||||||
|
// Iterate over all taps
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
// Evaluate the window at the adimensional parameter
|
||||||
|
data[i] = def(((float)(i + 1)) * ratio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void Window::apply(T* data, size_t len) const {
|
void Window::apply(T* data, size_t len) const {
|
||||||
// TODO
|
// Compute the linear map ratio
|
||||||
|
float ratio = 1.0f / ((float)(len + 1));
|
||||||
|
|
||||||
|
// Iterate over all taps
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
// Evaluate the window at the adimensional parameter
|
||||||
|
data[i] *= def(((float)(i + 1)) * ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template void Window::apply(float* data, size_t len) const;
|
||||||
|
//template void Window::apply(Complex* data, size_t len) const;
|
||||||
|
|
||||||
|
void Window::define() {
|
||||||
|
// Ensure an error is thrown if the undefined window is used
|
||||||
|
def = Window::undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Window::undefined(float x) {
|
||||||
|
// Called when a window function was not defined
|
||||||
|
throw std::runtime_error("Undefined window");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
41
dsp/window.h
41
dsp/window.h
@@ -1,20 +1,37 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <functional>
|
||||||
// TODO: Make something better, this sucks to use
|
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
|
/**
|
||||||
|
* Window function.
|
||||||
|
* This class is NOT thread-safe.
|
||||||
|
*/
|
||||||
class Window {
|
class Window {
|
||||||
public:
|
public:
|
||||||
|
// Default constructor
|
||||||
|
Window();
|
||||||
|
|
||||||
|
// Copy constructor
|
||||||
|
Window(const Window& b);
|
||||||
|
|
||||||
|
// Move constructor
|
||||||
|
Window(Window&& b);
|
||||||
|
|
||||||
// Virtual destructor
|
// Virtual destructor
|
||||||
virtual ~Window();
|
virtual ~Window();
|
||||||
|
|
||||||
|
// Copy assignement operator
|
||||||
|
Window& operator=(const Window& b);
|
||||||
|
|
||||||
|
// Move assignement operator
|
||||||
|
Window& operator=(Window&& b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the value of the window function.
|
* Compute the value of the window function.
|
||||||
* @param x Point at which to compute the window at. Must be bounded between 0 and 1.
|
* @param x Point at which to compute the window at. Must be bounded between 0 and 1.
|
||||||
* @return Value of the window bounded between 0 and 1.
|
* @return Value of the window bounded between 0 and 1.
|
||||||
*/
|
*/
|
||||||
virtual float operator()(float x) const = 0;
|
inline float operator()(float x) { return def(x); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a window of a given length.
|
* Generate a window of a given length.
|
||||||
@@ -30,5 +47,21 @@ namespace dsp {
|
|||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
void apply(T* data, size_t len) const;
|
void apply(T* data, size_t len) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Define the window function by setting the `def` member.
|
||||||
|
* MUST be overriden by all window functions.
|
||||||
|
*/
|
||||||
|
virtual void define();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The window function itself.
|
||||||
|
* This member MUST be initialized by all window functions.
|
||||||
|
*/
|
||||||
|
std::function<float(float)> def;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static float undefined(float x);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
5
dsp/window/all.h
Normal file
5
dsp/window/all.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "hann.h"
|
||||||
|
#include "rectangular.h"
|
||||||
|
#include "triangular.h"
|
||||||
|
#include "welch.h"
|
||||||
15
dsp/window/hann.cpp
Normal file
15
dsp/window/hann.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "hann.h"
|
||||||
|
#include "../constants.h"
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
Hann::Hann() {
|
||||||
|
define();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hann::define() {
|
||||||
|
def = [](float x) {
|
||||||
|
float y = sinf(DSP_PI*x);
|
||||||
|
return y*y;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
12
dsp/window/hann.h
Normal file
12
dsp/window/hann.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../window.h"
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
class Hann : public Window {
|
||||||
|
public:
|
||||||
|
Hann();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void define();
|
||||||
|
};
|
||||||
|
}
|
||||||
11
dsp/window/rectangular.cpp
Normal file
11
dsp/window/rectangular.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include "rectangular.h"
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
Rectangular::Rectangular() {
|
||||||
|
define();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rectangular::define() {
|
||||||
|
def = [](float x) { return 1.0f; };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,9 @@
|
|||||||
namespace dsp::window {
|
namespace dsp::window {
|
||||||
class Rectangular : public Window {
|
class Rectangular : public Window {
|
||||||
public:
|
public:
|
||||||
/**
|
Rectangular();
|
||||||
* Compute the value of the window function.
|
|
||||||
* @param x Point at which to compute the window at. Must be bounded between 0 and 1.
|
private:
|
||||||
* @return Value of the window bounded between 0 and 1.
|
void define();
|
||||||
*/
|
|
||||||
inline float operator()(float x) const { return 1.0f; }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
12
dsp/window/triangular.cpp
Normal file
12
dsp/window/triangular.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "triangular.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
Triangular::Triangular() {
|
||||||
|
define();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Triangular::define() {
|
||||||
|
def = [](float x) { return 1.0f - fabsf(2.0f*x - 1.0f); };
|
||||||
|
}
|
||||||
|
}
|
||||||
12
dsp/window/triangular.h
Normal file
12
dsp/window/triangular.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../window.h"
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
class Triangular : public Window {
|
||||||
|
public:
|
||||||
|
Triangular();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void define();
|
||||||
|
};
|
||||||
|
}
|
||||||
14
dsp/window/welch.cpp
Normal file
14
dsp/window/welch.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "welch.h"
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
Welch::Welch() {
|
||||||
|
define();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Welch::define() {
|
||||||
|
def = [](float x) {
|
||||||
|
float y = 2.0f*x - 1.0f;
|
||||||
|
return 1.0f - y*y;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
12
dsp/window/welch.h
Normal file
12
dsp/window/welch.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../window.h"
|
||||||
|
|
||||||
|
namespace dsp::window {
|
||||||
|
class Welch : public Window {
|
||||||
|
public:
|
||||||
|
Welch();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void define();
|
||||||
|
};
|
||||||
|
}
|
||||||
30
src/main.cpp
30
src/main.cpp
@@ -1,18 +1,26 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "dsp/buffer.h"
|
#include "dsp/window.h"
|
||||||
#include "dsp/taps.h"
|
#include "dsp/window/all.h"
|
||||||
#include "dsp/complex.h"
|
|
||||||
#include <volk/volk.h>
|
|
||||||
|
|
||||||
void testFunc(const float* buf, size_t len) {
|
void testFunc(dsp::Window win) {
|
||||||
|
printf("win(0.0) = %f\n", win(0.0));
|
||||||
};
|
printf("win(0.5) = %f\n", win(0.5));
|
||||||
|
printf("win(1.0) = %f\n", win(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
float* test;
|
try {
|
||||||
dsp::Taps<float> taps;
|
testFunc(dsp::window::Hann());
|
||||||
|
|
||||||
testFunc(((const dsp::Taps<float>&)taps).data(), taps.size());
|
dsp::Window win = dsp::window::Triangular();
|
||||||
|
dsp::Window win2 = dsp::window::Hann();
|
||||||
|
|
||||||
return 0;
|
win = dsp::window::Hann();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
fprintf(stderr, "ERROR: %s\n", e.what());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user