diff --git a/CMakeLists.txt b/CMakeLists.txt index 324c264..51765ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,15 @@ add_executable(${PROJECT_NAME} ${SRC}) target_include_directories(${PROJECT_NAME} PRIVATE "/") -target_compile_options(${PROJECT_NAME} PRIVATE /std:c++20) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) # Set compiler options if (MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /EHsc) # /O2 /Ob2 elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(${PROJECT_NAME} PRIVATE -O3) + target_compile_options(${PROJECT_NAME} PRIVATE -O3 -Wno-literal-suffix) else () - target_compile_options(${PROJECT_NAME} PRIVATE -O3) + target_compile_options(${PROJECT_NAME} PRIVATE -O3 -Wno-literal-suffix) endif () diff --git a/dsp/buffer.h b/dsp/buffer.h index de530e0..88fa0b5 100644 --- a/dsp/buffer.h +++ b/dsp/buffer.h @@ -1,5 +1,6 @@ #pragma once #include +#include // TODO: Force inline the data stuff for maximal performance in FIR @@ -103,6 +104,18 @@ namespace dsp { */ inline operator bool() const { return _capacity; } + /** + * Cast to sample pointer. + * @return Pointer to the samples + */ + inline operator T*() { return buffer; } + + /** + * Cast to const sample pointer. + * @return Const pointer to the samples + */ + inline operator const T*() const { return buffer; } + /** * Access a sample by index. * @param index Index of the sample. diff --git a/dsp/complex.h b/dsp/complex.h index aa85837..cdf28ba 100644 --- a/dsp/complex.h +++ b/dsp/complex.h @@ -8,26 +8,26 @@ namespace dsp { */ struct Complex { // Default constructor - constexpr inline Complex() {} + inline Complex() {} /** * Create a complex number from its real and imaginary components. * @param re Real component. * @param im Imaginary component. */ - constexpr inline Complex(float re, float im) { - this->re = re; - this->im = im; - } + constexpr inline Complex(float re, float im) : + re(re), + im(im) + {} /** * Create a complex number from a real value. * @param value Real component. */ - constexpr inline Complex(float value) { - re = value; - im = 0.0f; - } + constexpr inline Complex(float value) : + re(value), + im(0.0f) + {} /** * Compute the conjugate of the Complex number. @@ -114,6 +114,18 @@ namespace dsp { }; } +inline constexpr bool operator==(const dsp::Complex& a, const dsp::Complex& b) { + return a.re == b.re && a.im == b.im; +} + +inline constexpr bool operator==(const dsp::Complex& a, float b) { + return a.im == 0.0f && a.re == b; +} + +inline constexpr bool operator==(float a, const dsp::Complex& b) { + return b.im == 0.0f && b.re == a; +} + inline constexpr dsp::Complex operator+(const dsp::Complex& a, float b) { return dsp::Complex{ a.re + b, a.im }; } diff --git a/dsp/demod/fm.h b/dsp/demod/fm.h index 9e8108b..d6c29ba 100644 --- a/dsp/demod/fm.h +++ b/dsp/demod/fm.h @@ -1,10 +1,9 @@ #pragma once -#include "../processor.h" -#include "../worker.h" +#include #include "../complex.h" namespace dsp::demod { - class FM : SISOProcessor { + class FM { public: // Default constructor FM(); @@ -60,6 +59,4 @@ namespace dsp::demod { float gain = 0.0f; float lastPhase = 0.0f; }; - - using FMw = SISOWorker; } \ No newline at end of file diff --git a/dsp/filter/fir.cpp b/dsp/filter/fir.cpp index 665bad2..c2d15c5 100644 --- a/dsp/filter/fir.cpp +++ b/dsp/filter/fir.cpp @@ -1,5 +1,6 @@ #include "fir.h" #include "../complex.h" +#include #include namespace dsp::filter { @@ -83,6 +84,7 @@ namespace dsp::filter { return count; } + // Instantiate the class template class FIR; template class FIR; template class FIR; diff --git a/dsp/math/add.h b/dsp/math/add.h index 5d7903d..4cc015e 100644 --- a/dsp/math/add.h +++ b/dsp/math/add.h @@ -1,4 +1,6 @@ #pragma once +#include +#include namespace dsp::math { template diff --git a/dsp/multirate/decimating_fir.cpp b/dsp/multirate/decimating_fir.cpp deleted file mode 100644 index 596250a..0000000 --- a/dsp/multirate/decimating_fir.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "decimating_fir.h" -#include "../complex.h" -#include - -namespace dsp::multirate { - template - DecimatingFIR::DecimatingFIR() {} - - template - DecimatingFIR::DecimatingFIR(const Taps& taps, int decim) { - // Save the parameters - this->taps = taps; - this->decim = decim; - - // Pre-allocate the history buffer - hist.reserve(taps.size()); - - // Initialize the state - reset(); - } - - template - DecimatingFIR::DecimatingFIR(Taps&& taps, int decim) { - // Save the parameters - this->taps = taps; - this->decim = decim; - - // Pre-allocate the history buffer - hist.reserve(taps.size()); - - // Initialize the state - reset(); - } - - template - const Taps& DecimatingFIR::getTaps() const { - // Return the taps - return taps; - } - - template - void DecimatingFIR::setTaps(const Taps& taps, int decim) { - // Update the taps - this->taps = taps; - - // Update the decimation factor if it was given - if (decim) { this->decim = decim; } - } - - template - void DecimatingFIR::setTaps(Taps&& taps, int decim) { - // Update the taps - this->taps = taps; - - // Update the decimation factor if it was given - if (decim) { this->decim = decim; } - } - - template - void DecimatingFIR::reset() { - // Zero out the history buffer - memset(hist.data(), 0, (taps.size() - 1) * sizeof(SAMPLE)); - - // Reset the offset - offset = 0; - } - - template - size_t DecimatingFIR::process(const SAMPLE* in, SAMPLE* out, size_t count) { - // Reserve enough space in the history buffer - hist.reserve(taps.size() + count - 1, REALLOC_KEEP); - - // Copy over the new input samples - memcpy(&hist[taps.size() - 1], in, count * sizeof(SAMPLE)); - - // Filter the samples - uintptr_t i; - uintptr_t io = 0; - for (i = offset; i < count; i += decim) { - // Compute the dot product depending on type - if constexpr (std::is_same_v) { - volk_32f_x2_dot_prod_32f(&out[io++], &hist[i], taps.data(), taps.size()); - } - else if constexpr (std::is_same_v && std::is_same_v) { - volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out[io++], (const lv_32fc_t*)&hist[i], taps.data(), taps.size()); - } - else if constexpr (std::is_same_v && std::is_same_v) { - volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&out[io++], (const lv_32fc_t*)&hist[i], (const lv_32fc_t*)taps.data(), taps.size()); - } - } - - // Update the offset - offset = i - count; - - // Move over the unused sample to the history buffer - memmove(hist.data(), &hist[count], (taps.size() - 1) * sizeof(SAMPLE)); - - // Return the number of samples generated - return io; - } -} \ No newline at end of file diff --git a/dsp/multirate/decimating_fir.h b/dsp/multirate/decimating_fir.h deleted file mode 100644 index b780579..0000000 --- a/dsp/multirate/decimating_fir.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include "../processor.h" -#include "../buffer.h" -#include "../taps.h" - -namespace dsp::multirate { - template - class DecimatingFIR { - public: - // Default constructor - DecimatingFIR(); - - /** - * Create a decimating FIR filter. - * @param taps Taps to use for the filter. - * @param decim Decimation factor. - */ - DecimatingFIR(const Taps& taps, int decim); - - /** - * Create a decimating FIR filter. - * @param taps Taps to use for the filter. - * @param decim Decimation factor. - */ - DecimatingFIR(Taps&& taps, int decim); - - /** - * Get the filter taps. - * @return Filter taps. - */ - const Taps& getTaps() const; - - /** - * Set the filter taps. - * @param taps Filter taps. - * @param decim Decimation factor. Zero to keep unchanged. - */ - void setTaps(const Taps& taps, int decim = 0); - - /** - * Set the filter taps. - * @param taps Filter taps. - * @param decim Decimation factor. Zero to keep unchanged. - */ - void setTaps(Taps&& taps, int decim = 0); - - /** - * Get the decimation factor. - * @return Decimation factor. - */ - int getDecimation(); - - /** - * Set the decimation factor. - * @param decim Decimation factor. - */ - void setDecimation(int decim); - - /** - * Reset the state of the filter. - */ - void reset(); - - /** - * Filter and decimate samples. - * @param in Input samples. - * @param out Output samples. - * @param count Number of samples to filter. - * @return Number of samples generates. - */ - size_t process(const SAMPLE* in, SAMPLE* out, size_t count); - - private: - Buffer hist; - uintptr_t offset = 0; - Taps taps; - int decim = 0; - }; -} \ No newline at end of file diff --git a/dsp/multirate/interpolating_fir.cpp b/dsp/multirate/interpolating_fir.cpp deleted file mode 100644 index 0a75138..0000000 --- a/dsp/multirate/interpolating_fir.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "interpolating_fir.h" -#include "../complex.h" -#include - -namespace dsp::multirate { - template - InterpolatingFIR::InterpolatingFIR() {} - - template - InterpolatingFIR::InterpolatingFIR(const Taps& taps, int interp) { - // Save the parameters - this->taps = taps; - this->interp = interp; - - // Pre-allocate the history buffer - hist.reserve(taps.size()); - - // Initialize the state - reset(); - - // Generate the tap phases - generatePhases(); - } - - template - InterpolatingFIR::InterpolatingFIR(Taps&& taps, int interp) { - // Save the parameters - this->taps = taps; - this->interp = interp; - - // Pre-allocate the history buffer - hist.reserve(taps.size()); - - // Initialize the state - reset(); - - // Generate the tap phases - generatePhases(); - } - - template - const Taps& InterpolatingFIR::getTaps() const { - // Return the taps - return taps; - } - - template - void InterpolatingFIR::setTaps(const Taps& taps, int interp) { - // Update the taps - this->taps = taps; - - // Update the interpolation factor if it was given - if (interp) { this->interp = interp; } - - // Regenerate the tap phases - generatePhases(); - } - - template - void InterpolatingFIR::setTaps(Taps&& taps, int interp) { - // Update the taps - this->taps = taps; - - // Update the interpolation factor if it was given - if (interp) { this->interp = interp; } - - // Regenerate the tap phases - generatePhases(); - } - - template - void InterpolatingFIR::reset() { - // Zero out the history buffer - memset(hist.data(), 0, (taps.size() - 1) * sizeof(SAMPLE)); - - // Reset the offset - offset = 0; - } - - template - size_t InterpolatingFIR::process(const SAMPLE* in, SAMPLE* out, size_t count) { - // Reserve enough space in the history buffer - hist.reserve(taps.size() + count - 1, REALLOC_KEEP); - - // Copy over the new input samples - memcpy(&hist[taps.size() - 1], in, count * sizeof(SAMPLE)); - - // Filter the samples - uintptr_t i; - uintptr_t io = 0; - for (i = offset; i < count; i += decim) { - // Compute the dot product depending on type - if constexpr (std::is_same_v) { - volk_32f_x2_dot_prod_32f(&out[io++], &hist[i], taps.data(), taps.size()); - } - else if constexpr (std::is_same_v && std::is_same_v) { - volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out[io++], (const lv_32fc_t*)&hist[i], taps.data(), taps.size()); - } - else if constexpr (std::is_same_v && std::is_same_v) { - volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&out[io++], (const lv_32fc_t*)&hist[i], (const lv_32fc_t*)taps.data(), taps.size()); - } - } - - // Update the offset - offset = i - count; - - // Move over the unused sample to the history buffer - memmove(hist.data(), &hist[count], (taps.size() - 1) * sizeof(SAMPLE)); - - // Return the number of samples generated - return io; - } -} \ No newline at end of file diff --git a/dsp/multirate/interpolating_fir.h b/dsp/multirate/interpolating_fir.h deleted file mode 100644 index e2ac2bb..0000000 --- a/dsp/multirate/interpolating_fir.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once -#include "../processor.h" -#include "../buffer.h" -#include "../taps.h" -#include - -namespace dsp::multirate { - template - class InterpolatingFIR { - public: - // Default constructor - InterpolatingFIR(); - - /** - * Create a decimating FIR filter. - * @param taps Taps to use for the filter. - * @param interp Interpolation factor. - */ - InterpolatingFIR(const Taps& taps, int interp); - - /** - * Create a decimating FIR filter. - * @param taps Taps to use for the filter. - * @param interp Interpolation factor. - */ - InterpolatingFIR(Taps&& taps, int interp); - - /** - * Get the filter taps. - * @return Filter taps. - */ - const Taps& getTaps() const; - - /** - * Set the filter taps. - * @param taps Filter taps. - * @param interp Interpolation factor. Zero to keep unchanged. - */ - void setTaps(const Taps& taps, int interp = 0); - - /** - * Set the filter taps. - * @param taps Filter taps. - * @param interp Interpolation factor. Zero to keep unchanged. - */ - void setTaps(Taps&& taps, int interp = 0); - - /** - * Get the interpolation factor. - * @return Interpolation factor. - */ - int getInterpolation(); - - /** - * Set the interpolation factor. - * @param interp Interpolation factor. - */ - void setInterpolation(int interp); - - /** - * Reset the state of the filter. - */ - void reset(); - - /** - * Filter and decimate samples. - * @param in Input samples. - * @param out Output samples. - * @param count Number of samples to filter. - * @return Number of samples generates. - */ - size_t process(const SAMPLE* in, SAMPLE* out, size_t count); - - private: - void generatePhases(); - - Buffer hist; - uintptr_t offset = 0; - Taps taps; - std::vector> phases; - int interp = 0; - }; -} \ No newline at end of file diff --git a/dsp/processor.h b/dsp/processor.h index c8383ef..b1dc297 100644 --- a/dsp/processor.h +++ b/dsp/processor.h @@ -6,6 +6,10 @@ namespace dsp { template class SISOProcessor { public: + inline SISOProcessor() {} + + inline SISOProcessor(SISOProcessor&& b) {} + virtual size_t process(const T_IN* in, T_OUT* out, size_t count) = 0; inline size_t processSafe(const T_IN* in, T_OUT* out, size_t count) { diff --git a/dsp/sink/file.cpp b/dsp/sink/file.cpp index d37a719..c40629b 100644 --- a/dsp/sink/file.cpp +++ b/dsp/sink/file.cpp @@ -1,5 +1,6 @@ #include "file.h" #include "../complex.h" +#include // TODO: Thread safety diff --git a/dsp/source/file.cpp b/dsp/source/file.cpp index 8fe5a2f..703fb7b 100644 --- a/dsp/source/file.cpp +++ b/dsp/source/file.cpp @@ -1,5 +1,6 @@ #include "file.h" #include "../complex.h" +#include // TODO: Thread safety diff --git a/dsp/source/wgn.h b/dsp/source/wgn.h index 805fa4a..2482425 100644 --- a/dsp/source/wgn.h +++ b/dsp/source/wgn.h @@ -1,4 +1,6 @@ #pragma once +#include +#include namespace dsp::source { template diff --git a/dsp/taps.cpp b/dsp/taps.cpp index 3f056ef..79e7bc1 100644 --- a/dsp/taps.cpp +++ b/dsp/taps.cpp @@ -1,6 +1,7 @@ #include "taps.h" #include "complex.h" #include +#include namespace dsp { template diff --git a/dsp/taps.h b/dsp/taps.h index c44d116..6458535 100644 --- a/dsp/taps.h +++ b/dsp/taps.h @@ -37,7 +37,7 @@ namespace dsp { * Get the number of taps. * @return Number of taps. */ - inline size_t size() { return _size; } + inline size_t size() const { return _size; } /** * Get a pointer to the taps. diff --git a/src/main.cpp b/src/main.cpp index 8627acc..833503c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,71 +1,31 @@ #include #include -#include "dsp/buffer.h" -#include "dsp/demod/fm.h" -#include "dsp/sink/file.h" +#include #include #include - -#include "dsp/source/wgn.h" +#include "../dsp/buffer.h" +#include "../dsp/source/wgn.h" +#include "../dsp/demod/fm.h" +#include "../dsp/taps/low_pass.h" +#include "../dsp/mrate/fir_decim.h" +#include "../dsp/sink/file.h" #define BUFFER_SIZE 1250 -void sendWorker(dsp::Stream* input) { - // Generate a buffer of random data - dsp::source::WGN wgn(1.0); - dsp::Buffer randShit(BUFFER_SIZE); - wgn.source(randShit.data(), BUFFER_SIZE); - - while (true) { - // Get a buffer for sending - auto bset = input->reserve(BUFFER_SIZE); - - // Copy over the samples - memcpy(bset.buffer[0], randShit.data(), BUFFER_SIZE * sizeof(dsp::Complex)); - - // Send the samples - if (!input->send(BUFFER_SIZE)) { break; } - } -} - -std::atomic_uint64_t counter; -void receiveWorker(dsp::Stream* output) { - while (true) { - // Receive a buffer - auto bset = output->recv(); - if (!bset.samples) { break; } - - // Add to the counter - counter += bset.samples; - - // Flush the buffer - output->flush(); - } -} - int main() { try { - // Define the DSP - dsp::demod::FMw fmw = dsp::demod::FM(6125, 50e3); + dsp::source::WGN wgn(1.0f); + dsp::demod::FM fmd(6125, 240e3); + dsp::mrate::FIRDecim decim(dsp::taps::LowPass(20e3, 4e3, 240e3), 5); + dsp::sink::File file("output.f32"); - // Start the send worker - std::thread sendThread(sendWorker, fmw.in()); - std::thread receiveThread(receiveWorker, fmw.out()); + dsp::Buffer cbuf(BUFFER_SIZE); + dsp::Buffer rbuf(BUFFER_SIZE); - // Start the FM demod - fmw.start(); - - // Start receiving - while (true) { - // Get the number of samples processed - size_t proc = counter.exchange(0); - - // Print - printf("%lf MS/s\n", (double)proc / 1e6); - - // Wait - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } + wgn.source(cbuf, BUFFER_SIZE); + fmd.process(cbuf, rbuf, BUFFER_SIZE); + size_t ocount = decim.process(rbuf, rbuf, BUFFER_SIZE); + file.sink(rbuf, ocount); return 0; }