diff --git a/CHANGELOG b/CHANGELOG index 51ba9a6aff719e051c4521525f6e02ee2dccf20e..af143090f7ce0ca5295c60e6ba7bcc5f52811c1f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ TODO:: +- Add example with test of DSP::u::AdjustableDelay + + output difference between standard and cyclic buffer to check if both variants work the same - DSP::u::Zeroinserter => add variant with possibility to define number of inputs -- ADD: DSP::u::ControledDelay(max_delay, initial_delay, ...) SOUND_support_t::get_wave_in_raw_buffer - zastÄ piÄ metodÄ zawierajÄ cÄ konwersjÄ typu, ew. rozbudowaÄ konwersjÄ w DSP::u::AudioInput - test socket support on Linux (test examples as there are have been noticed some problems with bind) @@ -11,10 +12,12 @@ TODO:: LAST DONE: CHANGES: -- ver. 0.20.029 - - added: DSP::Block::DefineStandardInputs - - added: DSP::Component::DefineStandardOutputs - - added: DSP::u::RawDecimator - added variant with AreInputsComplex parameter +- ver. 0.20.030 <b>2023.11.10</b> Added: + - DSP::u::AdjustableDelay - delay block with programaticly controlable delay (betwee 0 and user defined max_delay). +- ver. 0.20.029 <b>2023.11.10</b> Added: + - DSP::Block::DefineStandardInputs + - DSP::Component::DefineStandardOutputs + - DSP::u::RawDecimator - new variant with AreInputsComplex parameter - ver. 0.20.028 - <b>2023.06.14</b> Fixed: - fixed gray coding in DSP::u::SymbolMapper - ver. 0.20.027 - <b>2023.04.21</b> Fixed: diff --git a/src/cpp/DSP_modules.cpp b/src/cpp/DSP_modules.cpp index 99054b63cd15ca53e504e431f4d66ebb3c3dfbdc..08e097f7cd80fc596e5c6d0baa367088661ccfc8 100644 --- a/src/cpp/DSP_modules.cpp +++ b/src/cpp/DSP_modules.cpp @@ -3621,6 +3621,301 @@ void DSP::u::Delay::InputExecute_with_cyclic_buffer_multi(INPUT_EXECUTE_ARGS) } #undef THIS + +// Adjustable delay element implemented in processing mode +// cannot separate processing in digital feedback loop !!! +DSP::u::AdjustableDelay::AdjustableDelay(unsigned int max_delay_in, unsigned int initial_delay, unsigned int InputsNo, bool IsBufferCyclic) : DSP::Block() +{ + unsigned int ind; + std::string temp; + std::vector<unsigned int> indexes; + + if (IsBufferCyclic == true) + SetName("Delay(cyclic)", false); + else + SetName("Delay", false); + + if (InputsNo <= 0) + InputsNo = 1; + SetNoOfOutputs(InputsNo); + SetNoOfInputs(InputsNo, false); + + DefineStandardInputs(false); + DefineStandardOutputs(false); + + ClockGroups.AddInputs2Group("all", 0, NoOfInputs-1); + ClockGroups.AddOutputs2Group("all", 0, NoOfOutputs-1); + + NoOfInputsProcessed=InitialNoOfInputsProcessed; + + max_delay=max_delay_in; + new_delay = initial_delay; + current_delay = new_delay; + + if (max_delay > 0) + { + State = new DSP::Float_ptr[NoOfInputs]; + index = new unsigned int[NoOfInputs]; + for (ind = 0; ind < NoOfInputs; ind++) + { + State[ind]=new DSP::Float[max_delay]; + memset(State[ind], 0, sizeof(DSP::Float) * max_delay); + index[ind]=0; + } + } + else + { + State = NULL; + index = NULL; + } + + if (InputsNo == 1) + { + Execute_ptr = &InputExecute; + if (IsBufferCyclic == true) + Execute_ptr = &InputExecute_with_cyclic_buffer; + if (max_delay == 0) + Execute_ptr = &InputExecute_D0; + if (max_delay == 1) + Execute_ptr = &InputExecute_D1; + } + else + { + Execute_ptr = &InputExecute_multi; + if (IsBufferCyclic == true) + Execute_ptr = &InputExecute_with_cyclic_buffer_multi; + if (max_delay == 0) + Execute_ptr = &InputExecute_D0_multi; + if (max_delay == 1) + Execute_ptr = &InputExecute_D1_multi; + } +} + +DSP::u::AdjustableDelay::~AdjustableDelay(void) +{ + unsigned int ind; + +// SetNoOfOutputs(0); + if (State != NULL) + { + for (ind = 0; ind < NoOfInputs; ind++) + { + if (State[ind] != NULL) + { + delete [] State[ind]; + State[ind] = NULL; + } + } + delete [] State; + State = NULL; + } + if (index != NULL) + { + delete [] index; + index = NULL; + } +} + +void DSP::u::AdjustableDelay::UpdateDelayIfNeeded(void) { + if (NoOfInputsProcessed == InitialNoOfInputsProcessed) { + current_delay = new_delay; + } + NoOfInputsProcessed++; + + if (NoOfInputsProcessed == NoOfInputs) { + NoOfInputsProcessed = InitialNoOfInputsProcessed; + } +} + +// Sets new value of the delay. +/* Returns actually set delay (will differ from new_delay it is is out of range) +*/ +unsigned int DSP::u::AdjustableDelay::SetDelay(unsigned int new_delay_in) { + if (new_delay_in <= max_delay) + new_delay = new_delay_in; + return new_delay; +} +// Returns current delay +unsigned int DSP::u::AdjustableDelay::GetCurrentDelay(void) { + return current_delay; +} + +#define THIS ((DSP::u::AdjustableDelay *)block) +void DSP::u::AdjustableDelay::InputExecute_D0(INPUT_EXECUTE_ARGS) +{ + UNUSED_ARGUMENT(InputNo); + UNUSED_DEBUG_ARGUMENT(Caller); + + // \note The delay cannot be adjusted + THIS->UpdateDelayIfNeeded(); + + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + value, block); +}; + +void DSP::u::AdjustableDelay::InputExecute_D0_multi(INPUT_EXECUTE_ARGS) +{ + UNUSED_DEBUG_ARGUMENT(Caller); + + // \note The delay cannot be adjusted + THIS->UpdateDelayIfNeeded(); + + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + value, block); +}; + +void DSP::u::AdjustableDelay::InputExecute_D1(INPUT_EXECUTE_ARGS) +{ + UNUSED_ARGUMENT(InputNo); + UNUSED_DEBUG_ARGUMENT(Caller); + + THIS->UpdateDelayIfNeeded(); + + if (THIS->current_delay == 0) { + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + value, block); + } + else { + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + *(THIS->State[0]), block); + } + + // update buffer + *(THIS->State[0])=value; +}; + +void DSP::u::AdjustableDelay::InputExecute_D1_multi(INPUT_EXECUTE_ARGS) +{ + UNUSED_DEBUG_ARGUMENT(Caller); + + THIS->UpdateDelayIfNeeded(); + + if (THIS->current_delay == 0) { + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + value, block); + } + else { + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + *(THIS->State[InputNo]), block); + } + + // update buffer + *(THIS->State[InputNo])=value; +}; + +void DSP::u::AdjustableDelay::InputExecute(INPUT_EXECUTE_ARGS) +{ + UNUSED_ARGUMENT(InputNo); + UNUSED_DEBUG_ARGUMENT(Caller); + + THIS->UpdateDelayIfNeeded(); + + if (THIS->current_delay == 0) { + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + value, block); + } + else { + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + THIS->State[0][THIS->max_delay - THIS->current_delay], block); + } + + // update buffer + memcpy(THIS->State[0], THIS->State[0]+1, sizeof(DSP::Float)*(THIS->max_delay-1)); + THIS->State[0][THIS->max_delay-1]=value; +}; + +void DSP::u::AdjustableDelay::InputExecute_multi(INPUT_EXECUTE_ARGS) +{ + UNUSED_DEBUG_ARGUMENT(Caller); + + THIS->UpdateDelayIfNeeded(); + + if (THIS->current_delay == 0) { + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + value, block); + } + else { + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + THIS->State[InputNo][THIS->max_delay - THIS->current_delay], block); + } + + // update buffer + memcpy(THIS->State[InputNo], THIS->State[InputNo]+1, sizeof(DSP::Float)*(THIS->max_delay-1)); + THIS->State[InputNo][THIS->max_delay-1]=value; +}; + +void DSP::u::AdjustableDelay::InputExecute_with_cyclic_buffer(INPUT_EXECUTE_ARGS) +{ + UNUSED_ARGUMENT(InputNo); + UNUSED_DEBUG_ARGUMENT(Caller); + + THIS->UpdateDelayIfNeeded(); + + if (THIS->current_delay == 0) { + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + value, block); + } + else { + // Calculate actual current_delay index + // THIS->index[0] points to max_delay + // (index[0] + max_delay) % max_delay == index[0] => delay <- max_delay + // (index[0] + max_delay -1) % max_delay => delay <- 1 = max_delay - (max_delay - 1) + // (index[0] + 1) % max_delay => delay <- max_delay - 1 + unsigned int current_index = + (THIS->index[0] + (THIS->max_delay - THIS->current_delay)) % THIS->max_delay; + + THIS->OutputBlocks[0]->EXECUTE_PTR( + THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0], + THIS->State[0][THIS->index[current_index]], block); + } + + // update buffer + THIS->State[0][THIS->index[0]]=value; + + THIS->index[0]++; + THIS->index[0] %= THIS->max_delay; +}; + +void DSP::u::AdjustableDelay::InputExecute_with_cyclic_buffer_multi(INPUT_EXECUTE_ARGS) +{ + UNUSED_DEBUG_ARGUMENT(Caller); + + THIS->UpdateDelayIfNeeded(); + + if (THIS->current_delay == 0) { + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + value, block); + } + else { + // Calculate actual current_delay index + unsigned int current_index = + (THIS->index[InputNo] + (THIS->max_delay - THIS->current_delay)) % THIS->max_delay; + + THIS->OutputBlocks[InputNo]->EXECUTE_PTR( + THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo], + THIS->State[InputNo][current_index], block); + } + + // update buffer + THIS->State[InputNo][THIS->index[InputNo]]=value; + + THIS->index[InputNo]++; + THIS->index[InputNo] %= THIS->max_delay; +} +#undef THIS + /**************************************************/ // addition node DSP::u::Addition::Addition(unsigned int NoOfRealInputs_in, unsigned int NoOfComplexInputs_in) diff --git a/src/cpp/DSP_modules2.cpp b/src/cpp/DSP_modules2.cpp index 8a375689fc8bc14333909bea613bc3bcb055768e..2fdb1b926e92421b83b03839fca8de71dfb99cb8 100644 --- a/src/cpp/DSP_modules2.cpp +++ b/src/cpp/DSP_modules2.cpp @@ -1793,8 +1793,8 @@ unsigned int getConstellation( unsigned int n = 0; for (unsigned int ind=0; ind < M; ind++) { n = ind ^ (ind >> 1); // convert to gray coding - constellation[ind].re = static_cast<DSP::Float>(cos(constellation_phase_offset+(DSP::M_PIx2*n)/DSP::Float(M))); - constellation[ind].im = static_cast<DSP::Float>(sin(constellation_phase_offset+(DSP::M_PIx2*n)/DSP::Float(M))); + constellation[ind].re = static_cast<DSP::Float>(cos(constellation_phase_offset+(DSP::M_PIx2*DSP::Float(n))/DSP::Float(M))); + constellation[ind].im = static_cast<DSP::Float>(sin(constellation_phase_offset+(DSP::M_PIx2*DSP::Float(n))/DSP::Float(M))); // stringstream ss; // ss << "constellation[" << ind << "]={" << std::setprecision(2) << constellation[ind].re << "," << constellation[ind].im << "}; n=" << n; diff --git a/src/include/DSP_lib.h b/src/include/DSP_lib.h index 8e952675a7596e7d3055c4e09ad21692f245577b..572213c44dc879ec47c67784151774213cdaa6dc 100644 --- a/src/include/DSP_lib.h +++ b/src/include/DSP_lib.h @@ -11,7 +11,7 @@ #define DSP_VER_MAJOR 0 #define DSP_VER_MINOR 20 -#define DSP_VER_BUILD 29 // !!! without zeroes before, else this will be treated as octal number +#define DSP_VER_BUILD 30 // !!! without zeroes before, else this will be treated as octal number #define DSP_VER_YEAR 2023 #define DSP_VER DSP_VER_MAJOR.DSP_VER_MINOR.DSP_VER_BUILD @@ -588,8 +588,8 @@ std::string DSP::lib_version_string(); * but they have also inputs. * * Basic block for separating input form output in feedback loops - * is the delay block DSP::u::LoopDelay. Note that there is also - * DSP::u::Delay block but it cannot be used in the feedback loop. + * is the delay block DSP::u::LoopDelay. Note that there are also + * DSP::u::Delay and DSP::u::AdjustableDelay blocks but they cannot be used in the feedback loop. * However DSP::u::Delay should be used in all other cases * because it is implemented in more efficient way than * DSP::u::LoopDelay. @@ -714,13 +714,13 @@ std::string DSP::lib_version_string(); * -# DSP::u::ABS calculates absolute value of real or complex sample * -# DSP::u::Accumulator implements standard accumulator or accumulator with leakage * -# DSP::u::Addition addition block (also weighted summation) + * -# DSP::u::AdjustableDelay variant of DSP::u::Delay with programaticaly controlable delay (between 0 and user defined max_delay). * -# DSP::u::Amplifier multiplies input value by given constant * -# DSP::u::Angle calculates the phase of a complex sample * -# DSP::u::CCPC CCPC - Cartesian coordinates to polar coordinates converter * -# DSP::u::CMPO CMPO - complex mutual power operator * -# DSP::u::Conjugation calculates complex conjugation - * -# DSP::u::CyclicDelay this block is OBSOLETE use DSP::u::Delay instead. - * -# DSP::u::Delay delay element implemented in processing block mode. Inefficient for large buffer (implemented using memcopy). See DSP::u::CyclicDelay. + * -# DSP::u::Delay delay element implemented as processing block. For large delays cyclic buffer variant (IsBufferCyclic = true) is much more efficient. * -# DSP::u::Differator Differator - first order backward difference operator * -# DSP::u::FIR FIR filter implementation * -# DSP::u::IIR IIR filter implementation diff --git a/src/include/DSP_modules.h b/src/include/DSP_modules.h index 69aab0c2f1bdeabe47f8165f9849ccb90c3b73b3..9bf24832426759b021abaa4248b1a0567c4fe13e 100644 --- a/src/include/DSP_modules.h +++ b/src/include/DSP_modules.h @@ -51,6 +51,7 @@ namespace DSP { namespace u { class Copy; class Delay; + class AdjustableDelay; class LoopDelay; class Splitter; class Amplifier; @@ -2039,6 +2040,62 @@ class DSP::u::Delay : public DSP::Block virtual ~Delay(void); }; +//! Adjustable delay element implemented in processing mode +/*! \warning Cannot separate processing in digital feedback loop !!! + * + * Inputs and Outputs names: + * - Output: + * -# "out" (vector of all outputs) + * -# "out1", "out2", ... + * -# "out.re" == "out1" + * -# "out.im" == "out2" + * . + * - Input: + * -# "in" (vector of all inputs) + * -# "in1", "in2", ... + * -# "in.re" == "in1" + * -# "in.im" == "in2" + * . + */ +class DSP::u::AdjustableDelay : public DSP::Block +{ + private: + unsigned int max_delay; + unsigned int current_delay; + unsigned int new_delay; // + DSP::Float **State; + //! current index in buffer + unsigned int *index; + + //! Updates current_delay at the begining of the clock's cycle as well as NoOfInputsProcessed + void UpdateDelayIfNeeded(void); + + //! version for Delay == 0 + static void InputExecute_D0(INPUT_EXECUTE_ARGS); + static void InputExecute_D0_multi(INPUT_EXECUTE_ARGS); + //! version for Delay == 1 + static void InputExecute_D1(INPUT_EXECUTE_ARGS); + static void InputExecute_D1_multi(INPUT_EXECUTE_ARGS); + //! version with memcpy + static void InputExecute(INPUT_EXECUTE_ARGS); + static void InputExecute_multi(INPUT_EXECUTE_ARGS); + //! version with cyclic buffer + static void InputExecute_with_cyclic_buffer(INPUT_EXECUTE_ARGS); + static void InputExecute_with_cyclic_buffer_multi(INPUT_EXECUTE_ARGS); + public: + //! Sets new value of the delay. + /*! Returns actually set delay (will differ from new_delay it is is out of range) + * delay value will be updated at the begining of new clock cycle + * before that the GetCurrentDelay returns the previous delay value. + */ + unsigned int SetDelay(unsigned int new_delay_in); + //! Returns current delay + unsigned int GetCurrentDelay(void); + + //! DSP::u::AdjustableDelay block constructor + AdjustableDelay(unsigned int max_delay_in = 1, unsigned int initial_delay = 0, unsigned int InputsNo = 1, bool IsBufferCyclic = true); + virtual ~AdjustableDelay(void); +}; /**************************************************/ //! Outputs input value to multiple outputs /*! Inputs and Outputs names: