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>
void xTaskSystemSimulate( void )
{
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 };
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>
void xTaskSystemSimulate( void )
{
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 },
};
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>
void xTaskSystemSimulate( void )
{
real_t num[] = { 0.1f 0.2f, 0.3f };
real_t den[] = { 1.0f, -0.85f, 0.02f };
for( ;; ) {
uk = BSP_InputGet();
yk = gc.exite( uk );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
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>
void xTaskSystemSimulate( void )
{
{ 0.1f, 0.2f, 0.3f },
{ 1.0f, -0.85f, 0.02f },
};
for( ;; ) {
uk = BSP_InputGet();
yk = gc.excite( ut );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
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>
void simulate() {
}
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 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>
void xTaskSystemSimulate( void )
{
constexpr real_t tau = 1.0_re;
constexpr real_t dt = 0.01_re;
std::chrono::milliseconds delay(static_cast<long long>( dt*1000 ) );
{ 0.0f, 1.0f, },
{ tau, 1.0f, },
};
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;
}
}