Documentation
Tools for embedded systems
Loading...
Searching...
No Matches
Recursive LTI Systems Evaluation by transfer functions.

qlibs::ltisys is a class that evaluates single-input, single-output (SISO) transfer function models in real-valued systems. A transfer function is a model that describes the frequency-dependent response of a linear time-invariant system, and this class can handle both continuous-time and discrete-time systems. qlibs::ltisys can be used for simulating dynamic systems and implementing filters, compensators, or controllers.

Continous-time transfer functions

Here, the transfer function \( G(s) \) is the linear mapping of the Laplace transform of the input, \( U(s)= \mathcal{L}[u(t)]\), to the Laplace transform of the output \( Y(s)= \mathcal{L}[y(t)]\).

\( G(s) = \frac{N(s)}{D(s)} = \frac{ b_{0}s^{n} + b_{1}s^{n-1} + b_{2}s^{n-2} + ... + b_{n} }{ s^{n} + a_{1}s^{n-1} + a_{2}s^{n-2} + ... + a_{n} } \)

\( N(s) \) and \( D(s) \) are the numerator and denominator polynomials in descending powers of \(s\), respectively.

To instantiate a continuous transfer function, you should define a variable of type qlibs::continuousSystem, two arrays of N+1 elements with the coefficients of the polynomials for both, the numerator and denominator, and finally, an array of type qlibs::continuousStates to hold the N states of the system. Then, you can call qlibs::continuousSystem::setup() to construct the system and set initial conditions. Subsequently, you can evaluate the system with a given input-signal by just calling qlibs::continuousSystem::excite() .

Attention
The user must ensure that the evaluation of the system is executed periodically at the required time step.

Example: Evaluate the given continuous transfer function

\( G(s) = \frac{ 2s^{2} + 3s + 6 }{ s^{3} + 6s^{2} + 11s + 16 } \)
#include <iostream>
#include <chrono>
#include <thread>
#include <qlibs.h>
using namespace qlibs;
void xTaskSystemSimulate( void )
{
const real_t dt = 0.05f; // Time step
std::chrono::milliseconds delay(static_cast<long long>( dt*1000 ) );
real_t num[] = { 0.0f, 2.0f, 3.0f, 6.0f };
real_t den[] = { 1.0f, 6.0f, 11.0f, 16.0f };
continuousStates<3> x = { 0.0f, 0.0f, 0.0f }; // n = 3
continuousSystem gc( num, den, xC, dt );
real_t ut, yt;
for( ;; ) {
ut = BSP_InputGet();
yt = gc.excite( ut );
std::this_thread::sleep_for(delay);
std::cout << "u(t) = " << ut << " y(t) = " << yt << std::endl;
}
}
A LTI continuous system object.
Definition ltisys.hpp:635
nState[order] continuousStates
Type to specify continuous states.
Definition ltisys.hpp:47
The qLibs++ library namespace.
Definition mat.hpp:18
float real_t
A type to instantiate a real variable double-precision of 64-bits IEEE 754.
Definition qlibs_types.hpp:43

Alternatively, you can also use a simpler definition by employing the qlibs::continuousTF structure as follows:

#include <iostream>
#include <chrono>
#include <thread>
#include <qlibs.h>
using namespace qlibs;
void xTaskSystemSimulate( void )
{
const real_t dt = 0.05f; // Time step
std::chrono::milliseconds delay(static_cast<long long>( dt*1000 ) );
{ 0.0f, 2.0f, 3.0f, 6.0f },
{ 1.0f, 6.0f, 11.0f, 16.0f },
};
continuousSystem gc( ctf, dt );
real_t ut, yt;
for( ;; ) {
ut = BSP_InputGet();
yt = gc.excite( ut );
std::this_thread::sleep_for(delay);
std::cout << "u(t) = " << ut << " y(t) = " << yt << std::endl;
}
}
Continuous transfer function for easy LTI system definition.
Definition ltisys.hpp:61

Discrete-time transfer functions

The z-transform is used in discrete-time systems to deal with the relationship between an input signal \( u(t) \) and an output signal \( y(t) \) , so the transfer function is similarly written as \( G(z^{-1}) \) and is often referred to as the pulse-transfer function.

\( G(z^{-1}) = \frac{N(z^{-1})}{D(z^{-1})} = \frac{ b_{0} + b_{1}z^{-1} + b_{2}z^{-2} + ... + b_{m}z^{-m} }{ 1 + a_{1}z^{-1} + a_{2}z^{-2} + ... + a_{n}z^{-n} } \)

Discrete systems are instantiated in a similar way to continuous systems, but there are some differences. States should be stored in an array of type qlibs::discreteStates. The size of polynomials can vary according to their order. Please take a look at the following example:

Example: Evaluate the given discrete transfer function

\( G(z^{-1}) = \frac{ 0.1 + 0.2z^{-1} + 0.3z^{-2} }{ 1 - 0.85z^{-1} + 0.02z^{-2} } \)
#include <iostream>
#include <chrono>
#include <thread>
#include <qlibs.h>
using namespace qlibs;
void xTaskSystemSimulate( void )
{
real_t num[] = { 0.1f 0.2f, 0.3f };
real_t den[] = { 1.0f, -0.85f, 0.02f };
discreteStates<3> xd = { 0.0f, 0.0f, 0.0f };
discreteSystem gc( num, den, xd );
real_t uk, yk;
for( ;; ) {
uk = BSP_InputGet();
yk = gc.exite( uk );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); /*100mS sample time*/
std::cout << "u(k) = " << uk << " y(k) = " << yk << std::endl;
}
}
A LTI discrete system object.
Definition ltisys.hpp:433
real_t[order] discreteStates
Type to specify discrete states.
Definition ltisys.hpp:53

Alternatively, you can also use a simpler definition by employing the qlibs::discreteTF structure as follows:

#include <iostream>
#include <chrono>
#include <thread>
#include <qlibs.h>
using namespace qlibs;
void xTaskSystemSimulate( void )
{
{ 0.1f, 0.2f, 0.3f },
{ 1.0f, -0.85f, 0.02f },
};
discreteSystem gc( dtf );
real_t uk, yk;
for( ;; ) {
uk = BSP_InputGet();
yk = gc.excite( ut );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); /*100mS sample time*/
std::cout << "u(k) = " << uk << " y(k) = " << yk << std::endl;
}
}
Discrete transfer function for easy LTI system definition.
Definition ltisys.hpp:276

Transport-Delay

The qlibs::transportDelay class models a "pure time delay" in a discrete-time system. It holds incoming samples in a buffer and outputs a sample from the past after a fixed number of discrete steps.

Mathematically, a transport delay can be expressed as:

\( y[k] = x[k - N] \)

where:

  • \( y[k] \) is the current output sample,
  • \( x[k] \) is the current input sample,
  • \( N \) is the number of discrete delay steps (numberOfDelays).

This is useful for:

  • Simulating signal propagation delays.
  • Implementing dead-time elements in control systems.
  • Testing systems where data arrival is intentionally delayed.

The delay amount is determined in discrete steps, not directly in seconds. To convert a time delay in seconds to discrete steps, you can use:

In simulation or discrete-time signal processing, a pure delay shifts the input signal in time without altering its shape or magnitude. In continuous-time systems, a pure time delay is represented by:

\( G(s) = e^{-sT_d} \)

where \( T_d \) is the delay time. When discretized for simulation (with a time step \( \Delta t \)), the delay becomes a **FIFO buffer** of length:

\( N = \frac{T_d}{\Delta t} \)

The qlibs::transportDelay class implements this buffer with:

  • Insertion of the newest sample at each step.
  • Extraction of the oldest stored sample as the delayed output.

This is especially important in control system simulations where transport delays affect stability and performance.

Basic Example

#include <qlibs.h>
using namespace qlibs;
constexpr real_t dt = 0.1_re; // 100 ms simulation step
transportDelay<3> myDelay; // Delay by 3 discrete steps (0.3 seconds)
void simulate() {
real_t inputSignal = 1.0_re;
real_t outputSignal = myDelay.delay(inputSignal);
// outputSignal contains the value of inputSignal from 0.3 seconds ago
}
Delays the input by a specified amount of time. You can use this class to simulate a time delay.
Definition ltisys.hpp:212
real_t delay(const real_t xInput) noexcept override
Delays the input by a specified amount of time.
Definition ltisys.hpp:234

Using Time Conversion

If you want a delay in seconds but only know the simulation step (dt), use qlibs::delayFromTime() or _td:

constexpr real_t dt = 0.1_re;
// Delay by 2.5 seconds
transportDelay<delayFromTime(2.5, dt)> myDelay1;
// Using the _td literal:
transportDelay<2.5_td(dt)> myDelay2;
// Using operator[] form:
transportDelay<4.3_td[dt]> myDelay3;
constexpr size_t delayFromTime(const real_t Time, const real_t dt)
Computes the number of discrete delays required for a specified amount of time using a defined time-s...
Definition ltisys.hpp:167

Example: Continuous System with Delay

This example simulates a first-order system with a pure transport delay. The system is:

\( G(s) = \frac{1}{\tau s + 1} \cdot e^{-sT_d} \)

The delay is implemented with qlibs::transportDelay after the continuous system output.

#include <iostream>
#include <chrono>
#include <thread>
#include <qlibs.h>
using namespace qlibs;
void xTaskSystemSimulate( void )
{
// Continuous system parameters
constexpr real_t tau = 1.0_re; // Time constant
constexpr real_t dt = 0.01_re; // Simulation step
constexpr real_t Td = 0.5_re; // Transport delay in seconds
std::chrono::milliseconds delay(static_cast<long long>( dt*1000 ) );
{ 0.0f, 1.0f, },
{ tau, 1.0f, },
};
continuousSystem gc( ctf, dt );
transportDelay<delayFromTime(Td, dt)> delayBlock;
real_t ut, yt;
for( ;; ) {
ut = BSP_InputGet();
yt = delayBlock( gc.excite( ut ) );
std::this_thread::sleep_for(delay);
std::cout << "u(t) = " << ut << " y(t) = " << yt << std::endl;
}
}