7.1.3 Transformada z unilateral e inversa con Sympy-Python

Referencia: Tabla de transformadas z: Lathi Tabla 5.1 Transformada z p492. Oppenheim tabla 10.2 p776, Schaum Hsu Tabla 4-1 p170. Propiedades:  Schaum Hsu Tabla 4-2 p173. Lathi Tabla 5.2 p509. Oppenheim Tabla 10.3 p793

Usando los algoritmos presentados para transformadas z a partir de la tabla y propiedades, se incorpora en la instrucción z_transform(f,n,z) incorporada en telg1001.py. Para demostración se desarrollan algunos ejemplos que se desarrollan a partir de la tabla de transformadas z.

Transformada z de impulso unitario δ(t)

>>> import sympy as sym
>>> import telg1001_z as fcnm
>>> n = sym.Symbol('n', real=True)
>>> z = sym.Symbol('z')
>>> d = sym.DiracDelta(n)
>>> f = d
>>> fcnm.z_transform(f,n,z)
1
>>>

Transformada z de impulso unitario desplazado δ(t-2) y δ(t+2)

>>> fcnm.z_transform(f.subs(n,n-2),n,z)
z**(-2)
>>> fcnm.z_transform(f.subs(n,n+2),n,z)
z**2
>>>

Tabla de Transformadas z unilateral e Inversas usando algoritmo en telg1001.py

Para comprobar las transformadas de los algoritmos presentados e integrados en telg1001.py, se crea una tabla con expresiones f[n] de prueba en el dominio n de tiempo discreto.

Al resultado se aplica la inversa de F[z] que muestra que la expresión es invertible usando la transformada z unilateral.

El resultado del algoritmo se muestra a continuación y luego el algoritmo.

z_transform :  0
  f[n]: DiracDelta(n)
  F[z]: (1, True, True)
  f[n]: (DiracDelta(n), True, True)

z_transform :  1
  f[n]: DiracDelta(n - 2)
  F[z]: (z**(-2), Abs(z) > 0, True)
  f[n]: (DiracDelta(n - 2), True, True)

z_transform :  2
  f[n]: DiracDelta(n + 2)
  F[z]: (z**2, True, True)
  f[n]: (DiracDelta(n + 2), True, True)

z_transform :  3
  f[n]: 3*DiracDelta(n + 2)
  F[z]: (3*z**2, True, True)
  f[n]: (3*DiracDelta(n + 2), True, True)

z_transform :  4
  f[n]: -3*DiracDelta(n + 2)
  F[z]: (-3*z**2, True, True)
  f[n]: (-3*DiracDelta(n + 2), True, True)

z_transform :  5
  f[n]: Heaviside(n)
  F[z]: (z/(z - 1), Abs(z) > 1, True)
  f[n]: (Heaviside(n), Abs(z) > 1, True)

z_transform :  6
  f[n]: Heaviside(n - 2)
  F[z]: (1/(z*(z - 1)), Abs(z) > 1, True)
  f[n]: [None, [1/(z*(z - 1))]]

z_transform :  7
  f[n]: Heaviside(n + 2)
  F[z]: (z**3/(z - 1), Abs(z) > 1, True)
  f[n]: (Heaviside(n + 2), Abs(z) > 1, True)

z_transform :  8
  f[n]: 3*Heaviside(n + 2)
  F[z]: (3*z**3/(z - 1), Abs(z) > 1, True)
  f[n]: (3*Heaviside(n + 2), Abs(z) > 1, True)

z_transform :  9
  f[n]: n*Heaviside(n)
  F[z]: (z/(z - 1)**2, Abs(z) > 1, True)
  f[n]: (n*Heaviside(n), Abs(z) > 1, True)

z_transform :  10
  f[n]: n**2*Heaviside(n)
  F[z]: (z*(z + 1)/(z - 1)**3, Abs(z) > 1, True)
  f[n]: (n**2*Heaviside(n), Abs(z) > 1, True)

z_transform :  11
  f[n]: n**3*Heaviside(n)
  F[z]: (z*(z**2 + 4*z + 1)/(z - 1)**4, Abs(z) > 1, True)
  f[n]: (n**3*Heaviside(n), Abs(z) > 1, True)

z_transform :  12
  f[n]: 0.5**n*Heaviside(n)
  F[z]: (z/(z - 0.5), Abs(z) > 0.5, True)
  f[n]: (0.5**n*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  13
  f[n]: 0.5**n*Heaviside(n)
  F[z]: (z/(z - 0.5), Abs(z) > 0.5, True)
  f[n]: (0.5**n*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  14
  f[n]: Heaviside(n)/4**n
  F[z]: (z/(z - 1/4), Abs(z) > 1/4, True)
  f[n]: (Heaviside(n)/4**n, Abs(z) > 1/4, True)

z_transform :  15
  f[n]: -0.5**n*Heaviside(-n - 1)
  F[z]: (z/(z - 0.5), Abs(z) < 0.5, True) f[n]: (0.5**n*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  16
  f[n]: 0.5**(n - 1)*Heaviside(n - 1)
  F[z]: (1.0/(z - 0.5), Abs(z) > 0.5, True)
  f[n]: [None, [1/(z - 0.5)]]

z_transform :  17
  f[n]: 4**(1 - n)*Heaviside(n - 1)
  F[z]: (1/(z - 1/4), Abs(z) > 1/4, True)
  f[n]: [None, [1/(z - 1/4)]]

z_transform :  18
  f[n]: 0.5**n*n*Heaviside(n)
  F[z]: (0.5*z/(z - 0.5)**2, Abs(z) > 0.5, True)
  f[n]: (0.5**n*n*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  19
  f[n]: -0.5**n*n*Heaviside(-n - 1)
  F[z]: (0.5*z/(z - 0.5)**2, Abs(z) < 0.5, True) f[n]: (0.5**n*n*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  20
  f[n]: 0.5**n*(n + 1)*Heaviside(n)
  F[z]: z/(z - 0.5) + 0.5*z/(z - 0.5)**2
  f[n]: (2*0.5**(n + 1)*(n + 1)*Heaviside(n + 1), Abs(z) > 0.5, True)

z_transform :  21
  f[n]: 0.5**n*n**2*Heaviside(n)
  F[z]: (0.5*z*(z + 0.5)/(z - 0.5)**3, Abs(z) > 0.5, True)
  f[n]: (0.5**n*n**2*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  22
  f[n]: 1.33333333333333*0.5**n*n*(n - 2)*(n - 1)*Heaviside(n)
  F[z]: 1.33333333333333*z/(z - 0.5)**2 - 2*z*(z + 0.5)/(z - 0.5)**3 + 0.666666666666667*z*(z**2 + 2*z + 0.25)/(z - 0.5)**4
  f[n]: (1.33333333333333*0.5**n*n*(n - 2)*(n - 1)*Heaviside(n), Abs(z) > 1, 0)

z_transform :  23
  f[n]: cos(2*n)*Heaviside(n)
  F[z]: (z*(z - cos(2))/(z**2 - 2*z*cos(2) + 1), Abs(z) > 1, True)
  f[n]: (cos(2*n)*Heaviside(n), Abs(z) > 1, True)

z_transform :  24
  f[n]: 0.5**n*cos(2*n)*Heaviside(n)
  F[z]: (z*(z - 0.5*cos(2))/(z**2 - z*cos(2) + 0.25), Abs(z) > 0.5, True)
  f[n]: (0.5**n*cos(2*n)*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  25
  f[n]: sin(2*n)*Heaviside(n)
  F[z]: (z*sin(2)/(z**2 - 2*z*cos(2) + 1), Abs(z) > 1, True)
  f[n]: (sin(2*n)*Heaviside(n), Abs(z) > 1, True)

z_transform :  26
  f[n]: 0.5**n*sin(2*n)*Heaviside(n)
  F[z]: (0.5*z*sin(2)/(z**2 - z*cos(2) + 0.25), Abs(z) > 0.5, True)
  f[n]: (0.5**n*sin(2*n)*Heaviside(n), Abs(z) > 0.5, True)

z_transform :  27
  f[n]: cos(2*n + 0.25)*Heaviside(n)
  F[z]: (z*(z*cos(0.25) - cos(1.75))/(z**2 - 2*z*cos(2) + 1), Abs(z) > 1, True)
  f[n]: (1.0*cos(2*n + 0.25)*Heaviside(n), Abs(z) > 1, True)

z_transform :  28
  f[n]: 3*0.5**n*cos(2*n + 0.25)*Heaviside(n)
  F[z]: (3*z*(z*cos(0.25) - 0.5*cos(1.75))/(z**2 - z*cos(2) + 0.25), Abs(z) > 0.5, True)
  f[n]: (3.0*0.5**n*cos(2*n + 0.25)*Heaviside(n), Abs(z) > 0.5, True)

Algoritmo en Python

Las intrucciones se pueden modificar para observar una expresión en detalle si se activa la variable sym.SYMPY_DEBUG=True. Por la extensión del resultado se recomienda activarla para una expresión.

import sympy as sym
import telg1001 as fcnm
sym.SYMPY_DEBUG = False

# INGRESO
n = sym.Symbol('n')
z = sym.Symbol('z')
u = sym.Heaviside(n)
d = sym.DiracDelta(n)

a0 = sym.Rational(1,4).limit_denominator(100)

tabla = [
           d,
           d.subs(n,n-2),
           d.subs(n,n+2),
           3*d.subs(n,n+2),
           -3*d.subs(n,n+2),
           u,
           u.subs(n,n-2),
           u.subs(n,n+2),
           3*u.subs(n,n+2),
           n*u,
           (n**2)*u,
           (n**3)*u,
           0.5**n*u,((1/2)**n)*u,(a0**n)*u,
           -(0.5**n)*u.subs(n,-n-1),
           (0.5**(n-1))*u.subs(n,n-1),
           (a0**(n-1))*u.subs(n,n-1),
           n*(0.5**n)*u,
           -n*(0.5**n)*u.subs(n,-n-1),
           (n+1)*(0.5**n)*u,
           n**2*(0.5**n)*u,
           n*(n-1)*(n-2)*(0.5**n)*u/((0.5**3)*6),
           sym.cos(2*n)*u,
           (0.5**n)*sym.cos(2*n)*u,
           sym.sin(2*n)*u,
           (0.5**n)*sym.sin(2*n)*u,
           sym.cos(2*n+0.25)*u,
           3*(0.5**n)*sym.cos(2*n+0.25)*u
           ]
# seleccionar revisar
revisar = tabla[:]

# revisa detalle de una transformada y la inversa
if len(revisar)<2:
    sym.SYMPY_DEBUG = True
else:
    sym.SYMPY_DEBUG = False

# PROCEDIMIENTO
for i in range(0,len(revisar),1):
    f = revisar[i]
    # SALIDA
    print('\nz_transform : ', i)
    print('  f[n]:',f)
    Fz = fcnm.z_transform(f,n,z)
    print('  F[z]:',Fz)
    if type(Fz)==tuple:
        fn = fcnm.inverse_z_transform(Fz[0],z,n)
    else:
        fn = fcnm.inverse_z_transform(Fz,z,n)
    print('  f[n]:',fn)

Tabla de Fórmulas Matemáticas

Referencia: Lathi Sec. B p54

B.8-3 Sumatorias

\sum_{k=m}^{n} r^k = \frac{r^{n+1}-r^m}{r-1} \texttt{ , } r\neq 1 \sum_{k=0}^{n} k = \frac{n(n+1)}{2} \sum_{k=0}^{n} k^2 = \frac{n(n+1)(2n+1)}{6} \sum_{k=0}^{n} k r^k = \frac{r+[n(r-1)-1]r^{n+1}}{(r-1)^2} \texttt{ , } r\neq 1 \sum_{k=0}^{n} k^2 r^k = \frac{r[(1+r)(1-r^n)-2n(1-r)r^n - n^2(1-r)^2 r^n] }{(1-r)^3} \texttt{ , } r\neq 1

 

7.1.2 Transformada z – Propiedades con Sympy-Python

Luego de intentar obtener la Transformada z con la Tabla de Pares f[n] y F[z], si del resultado sigue siendo None se busca aplicar las reglas descritas en la tabla de propiedades de la Transformada z.

Referencia: Schaum Hsu Tabla 4-2 p173. Lathi Tabla 5.2 p509. Oppenheim Tabla 10.3 p793


1. Propiedad de multiplicación por n de la transformada z

Presentada en la tabla de propiedades y también conocida como Diferenciación en z, se presenta como:

n x[n] u[n] -z \frac{\delta}{\delta z}X(z) R’ = R

Se aplica sobre la señal x[n]=1

x[n] = n \mu[n]

A partir de la tabla de transformadas se tiene que la expresión para μ[n] es

F[z] = \frac{z}{z-1} = (z)\frac{1}{z-1} \frac{\delta}{\delta z}F[z] = \frac{1}{z-1}-\frac{z}{(z-1)^2}

por lo que para X[z] de obtiene como:

X[z] = -z \frac{\delta}{\delta z} F[z] = -z \Big[ \frac{1}{z-1}-\frac{z}{(z-1)^2}\Big] = -z \Big[ -\frac{1}{(z-1)^2}\Big] X[z] = \frac{z}{(z-1)^2}

Algoritmo en Python

El desarrollo del algoritmo es posterior a la búsqueda en la tabla de pares de transformadas z, por lo que se importa el proceso desde telg1001.py. En la tabla, para comprobar el funcionamiento de la propiedad con los resultados del texto, se usa una tabla básica, que luego puede ser ampliada.

El algoritmo se desarrolla para una expresión básica como la de la tabla. Luego los procedimientos se integran en una sola instrucción tal como z_transform(f,n,z) que usa primero la tabla, luego revisa las propiedades y fórmula de la transformada para mostrar un resultado.

u = sym.Heaviside(n)

#f = u
f = n*u

Resultados con el algoritmo:

 k ; func: 1  ;  n*Heaviside(n)
no hubo expresión similar en tabla de transformadas
buscando aplicar una propiedad n*f[n]
  f_powna ; n**a: n  ;  {a_: 1}

 _z_propiedad n*f[n] -------------
  n**a ; func/n, : n  ;  Heaviside(n)
_z_pairs_table match:
  f:           Heaviside(n)
  z_pair f[n]: Heaviside(n)
  z_pair F[z]: z/(z - 1)
  try,check: True -> True
  _z_propiedad n*f[n] <--> -z*diff(Fz):
   (z/(z - 1)**2, Abs(z) > 1, True)
>>> 

Instrucciones en Python

El coeficiente constante k se separa de la expresión para simplificar el análisis de la expresión en func. La constante se reincorpora al resultado al final.

El procedimiento con la tabla de pares de transformadas  z_pairs_properties se obtiene desde telg1001.py , se usa con el parámetro apply_properties=False para mostrar el funcionamiento del algoritmo para el ejercicio. la instrucción z_pairs_properties hace el llamado a una función que analiza la expresión con las propiedades ya implementadas y se omite su escritura en condiciones normales.

Las expresiones pueden tener dos factores con exponentes: an, na, que corresponden a dos propiedades diferentes. El factor  de interés para el ejercicio es na.  La búsqueda de éstos factores se realiza con las instrucciones sym.pow(n,a) y sym.pow(a,n), en la variable f_powna que se inicializan con sym.S.One.

Cuando se encuentra la expresión con f_powna se modifica expresión func_n =func/n, con la que se vuelve a buscar el resultado en la tabla. El proceso para analizar la expresión puede realizarse de forma recursiva hasta obtener una expresión simple como μ[n] y regresar al estado anterior para completar las operaciones. La forma recursiva requiere implementar el procedimiento con def z_propiedades().

La aplicación de la propiedad de multiplicación para n(x[n]) prepara la expresión para aplicar:

n x[n] \longleftrightarrow -z \frac{\delta}{\delta z}X(z)
Fz = (k*(-z)*sym.factor(sym.diff(Fz[0],z,1)),
                  Fz[1], Fz[2])

que contiene las partes de la transformada como el plano de convergencia y condiciones de aplicación que también deben tomarse en cuenta durante el proceso. Aunque para expresiones se sumas, no se toman en cuenta para el resultado final como en los ejercicios de los textos de referencia.

Esta sección expone cómo se implementa la propiedad de transformada z, el resultado final se encuentra en el archivo.py.

# transformada z propiedades con Sympy
# aplicar luego de buscar en tabla de pares
import sympy as sym
from telg1001 import z_pairs_properties

# INGRESO
n = sym.Symbol('n', real=True)
z = sym.Symbol('z')
u = sym.Heaviside(n)

#f = u
f = n*u
#f = sym.cos(2*n)
#f = sym.sin(2*n)

# PROCEDIMIENTO
f = sym.expand(sym.powsimp(f))
a = sym.Wild('a', exclude=[n])
b = sym.Wild('b', exclude=[n])
y = sym.Wild('y')
g = sym.WildFunction('g', nargs=1)

# separa constantes del término
k, func = f.as_independent(n, as_Add=False)
print(' k ; func:',k,' ; ',func)
Fz = z_pairs_properties(func, n, z, apply_properties=False)
if not(Fz==None):
    Fz = k*Fz
    print(' usando tabla de pares: \n ',Fz)

if Fz==None: # no encontrada en tabla
    print('no hubo expresión similar en tabla de transformadas')
    print('buscando aplicar una propiedad n*f[n]')
    # crear una función para aplicar varias veces las propiedades
    # en las expresiones, en forma recursiva.
    
    # busca factores pow(n,a) or pow(a,n)
    f_powna = sym.S.One ; f_powan = sym.S.One
    factor_Mul = sym.Mul.make_args(func)
    for factor_k in factor_Mul:
        ma_powna = factor_k.match(sym.Pow(n,a))
        ma_powan = factor_k.match(sym.Pow(a,n))
        if ma_powna and not(ma_powna[a]==sym.S.Zero) and ma_powna[a].is_integer:
            f_powna = f_powna*factor_k
            print('  f_powna ; n**a:',factor_k,' ; ',ma_powna)
        if ma_powan:
            f_powan = f_powan*factor_k
            print('  f_powan ; a**n:',factor_k,' ; ',ma_powan)

    # otras formas de expresión a revisar
    ma_un = func.match(sym.Heaviside(y))
    ma_gn = func.match(g)
    ma_gu = func.match(g*sym.Heaviside(y))

    # z_propiedad de multipliación n*f[n] <--> -z*dF(z)/dz --------
    if not(f_powna==sym.S.One): # n**a factor encontrado
        ma_powna = f_powna.match(sym.Pow(n,a))
        func_n = func/n # aplicar luego de forma recursiva
        print('\n _z_propiedad n*f[n] -------------')
        print('  n**a ; func/n, :',n,' ; ',func_n)
        Fz = z_pairs_properties(func_n,n,z,
                                apply_properties=False)
        if not(Fz==None):
            Fz = (k*(-z)*sym.factor(sym.diff(Fz[0],z,1)),
                  Fz[1], Fz[2])
            print('  _z_propiedad n*f[n] <--> -z*diff(Fz):\n  ',Fz)

if Fz==None: # no encontrada en tabla, tampoco con la propiedad
    print('propiedad implementada no es suficiente')
    print('implementar otras propiedades.')

Un procedimiento semejante se implementa para la transformada z inversa.

2. Propiedad de multiplicación por n de la transformada z inversa

Se aplica a partir de F(z), donde la expresión será tipo polinomio, por lo que se prepara la expresión para conocer el grado del numerador y de nominador, separar su signo, constante de multiplicación, coeficientes, etc.

Para el resultado del ejercicio anterior f(n) = nμ(n) se obtuvo la expresión:

F[z] = \frac{z}{(z-1)^2}
n x[n] u[n] -z \frac{\delta}{\delta z}X(z) R’ = R

el sentido inverso de la propiedad, se aplica con un integral sobre la expresión con denominador Q=(z-a)b. y a partir de la respuesta F[z] con al menos el grado del numerador P de 1.

El integral se desarrolla con la expresión despejada de la derivada y con un valor F(0)=0 para determinar la constante del integral.

X[z] = \int{\Big[\frac{z}{(z-1)^2}\Big]\Big[\frac{-1}{z}\Big]} + C X[z] = \frac{1}{(z-1)} + C

usando el valor de para la constante F(0)=0

0 = \frac{1}{(0-1)} + C C = 1 X[z] = \frac{1}{(z-1)} +1 = \frac{1+(z-1)}{(z-1)} X[z] = \frac{z}{(z-1)}

de la tabla la transformada z inversa es μ(n), y al aplicar al resultado la propiedad se tiene:

x[n] = n \mu [n]

resultados con el algoritmo:

 Parametros de P/Q -------------
 P: z
 Q: (z - 1)**2
 P_signo ;k :  1  ;  1
 P_grado, P_ceros: 1  ;  {0: 1}
 Q_grado, Q_polos (real):  2  ;  [1, 1]
 ma_P1 (a*z+ b) :  {a_: 1, b_: 0}
 ma_Q1 (z-a)**b :  {a_: 1, b_: 2}
 ma_Q2 a*z**2+ b*z + r**2:  None
 Fz :  z/(z - 1)**2

 _z_propiedad multiplicación nf[n] <--> -z*diff(F[z])-- 
 Func = integrate(factor(Fz)/z,z):
  z/(z - 1)
_z_pairs_table match:
  k ; F:      1 z/(z - 1)
  z_pair F[z]: z/(z - 1)
         ma_z: {}
  z_pair f[n]: Heaviside(n)
  try,check: True -> True
  _z_propiedad multiplicación nf[n]:
  (n*Heaviside(n), Abs(z) > 1, True)
>>> 

Instrucciones en Python

# transformada z propiedades con Sympy
# aplicar luego de buscar en tabla de pares
import sympy as sym
from telg1001 import z_pairs_prop_inverse

# INGRESO
n = sym.Symbol('n', real=True)
z = sym.Symbol('z')
u = sym.Heaviside(n)

# multiplicación nf[n] <--> -z*diff(F[z])
#F = z/(z - 1) #f = u
F = z/(z - 1)**2 #f = n*u
#F = z*(z + 1)/(z - 1)**3 # (n**2)*u

# PROCEDIMIENTO
# separa constantes del término
fn = z_pairs_prop_inverse(F, z, n, apply_properties=False)
if not(fn==None):
    print(' usando tabla de pares: \n ',fn)

if fn==None: # no encontrada en tabla
    Fz = sym.simplify(F) #sym.simplify(F)
    a = sym.Wild('a', exclude=[n,z])
    b = sym.Wild('b', exclude=[n,z])
    r = sym.Wild('r', exclude=[n,z])
    y = sym.Wild('y')
    g = sym.WildFunction('g', nargs=1)
    # analiza como polinomio F[z]:
    # P_signo, constante, F[z] pares
    [P,Q] = F.as_numer_denom()
    # P
    P = sym.Poly(P,z)
    ma_P1 = P.match(a*z+ b)
    P_zeros  = sym.roots(P)
    P_degree = sym.degree(P,z)
    P_leadcoef = sym.LC(P)
    k = sym.Abs(P_leadcoef)
    P_sign = P_leadcoef/k
    P = P/P_leadcoef
    # Q
    Q = sym.factor(Q)
    ma_Q1 = Q.match((z-a)**b)
    ma_Q2 = Q.match(a*z**2+ b*z + r**2)
    Q_poles = sym.real_roots(Q)
    Q_degree = sym.degree(Q,z)
    # separa constante
    Fz = sym.factor(Fz*P_sign/k)
    print('\n Parametros de P/Q -------------')
    print(' P:',P)
    print(' Q:',Q)
    print(' P_signo ;k : ',P_sign,' ; ',k)
    print(' P_grado, P_ceros:',P_degree,' ; ', P_zeros)
    print(' Q_grado, Q_polos (real): ',Q_degree,' ; ',Q_poles)
    print(' ma_P1 (a*z+ b) : ',ma_P1)
    print(' ma_Q1 (z-a)**b : ',ma_Q1)
    print(' ma_Q2 a*z**2+ b*z + r**2: ',ma_Q2)
    print(' Fz : ',Fz)
    
    # _z_property nf[n] <--> -z*diff(F[z])
    if ma_Q1 and ma_Q1[a]==1 and P_degree>0:
        FuncI = sym.factor(Fz/(-z),z)
        Func = sym.integrate(FuncI,z)
        F0 = 0 # para Constante de integral
        C = -Func.subs(z,0)+F0 
        FunC = sym.factor(Func+C)
        print('\n _z_propiedad multiplicación nf[n] <--> -z*diff(F[z])-- ')
        print(' Func = integrate(factor(Fz)/z,z):\n ',FunC)
        fn = z_pairs_prop_inverse(FunC, z, n, apply_properties=True)
        if not(fn==None):
            fn = (P_sign*k*n*fn[0],fn[1],fn[2])
            print('  _z_propiedad multiplicación nf[n]:\n ',fn)

if fn==None: # no encontrada en tabla, tampoco con la propiedad
    print('propiedad implementada no es suficiente')
    print('implementar otras propiedades.')

Si la propiedad debe aplicarse más de una vez, será necesario convertir el bloque a una función para hacer llamadas recursivas a si misma, como es el caso de n2*u

F = z*(z + 1)/(z - 1)**3 # (n**2)*u

o al final de la sección de la propiedad, usar apply_properties=True, para usar el algoritmo implementado en telg1001_z .

7.1.1 Transformada z – Pares f[n] y F[z] con match() de Sympy-Python

Para facilitar el uso de la Transformada z se usa la Tabla de Pares f[n] y F[z], donde se busca una expresión semejante que permite cambiar del dominio_n al dominio_z. Los Pares se usan en conjunto con la tabla de propiedades de la Transformada z con lo que se extiende las posibilidades de uso para la tabla.

Se propone primero usar la tabla de pares con Sympy para buscar las expresiones semejantes, usando la instrucción ‘f.match()‘, donde f es la función en el dominio_n y F es la función en el domino_z. En la siguiente sección se incorpora la tabla de propiedades, para en conjunto realizar una búsqueda más completa de las transformadas z o de sus inversas.

Referencia: Schaum Hsu Tabla 4-1 p170. Lathi Tabla 5.1 Transformada z p492. Oppenheim tabla 10.2 p776


Ejercicio 1. transformada z de f[n] = cos(2n) con Sympy y la instrucción f.match()

Revise si existe una expresión para f[n] en una pequeña lista de pares de transformadas:

f[n] = \cos [2n]

Algoritmo en Python

El algoritmo inicia con las variables a usar para cada dominio y la expresión de Heaviside como en los ejemplos de las unidades anteriores.

En la tabla se requiere unas variables tipo «comodín» (Wild) para buscar la semejanza de las expresiones, se usan como sym.Wild('a', exclude=[n]) de la que se excluye la variable de tiempo discreto n.

Se incluye una pequeña lista como tabla de pares de transformadas como un ejemplo básico, que luego puede ser ampliada. Los pares se incluyen como tuplas con las expresiones para cada domino.

Para encontrar semejanzas, se recorre cada par de transformadas, comparando las expresiones con f.match(par_nz[0]), que para revisar la expresión del domino n, solo toma la primera parte de la tupla. Si existe coincidencia, se crea un diccionario que indica los valores comodín que hacen que las expresiones sean iguales.

Resultado del algoritmo

 se encontró expresión similar:
  f:            cos(2*n)
  z_pares f[n]: cos(n*a_)
   similar con: {a_: 2}
  z_pares F[z]: (z**2 - z*cos(a_))/(z**2 - 2*z*cos(a_) + 1)
  Fz:         : (z**2 - z*cos(2))/(z**2 - 2*z*cos(2) + 1)
>>> 

Instrucciones en Python

# transformada z con f.match de Sympy
# expresiones similares o semejantes f.match
import sympy as sym

# INGRESO
n = sym.Symbol('n', real=True)
z = sym.Symbol('z')
u = sym.Heaviside(n)

#f = u
#f = 5*u
f = sym.cos(2*n)
#f = sym.sin(2*n)

# PROCEDIMIENTO
# para revisar semejanza de expresiones fn y n_dom
a = sym.Wild('a', exclude=[n])
b = sym.Wild('b', exclude=[n])

# tabla de pares [(n_dom, z_dom)]
z_pares = [
    # impulso unitario d[n], DiracDelta
    (DiracDelta(n),
     S.One,
     S.true, S.Zero, dco),
    (DiracDelta(a*n),
     S.One,
     Abs(a)>0, S.Zero, dco),
    # escalon unitario u[n], Heaviside
    (Heaviside(n),
     z/(z-1),
     S.true, Abs(z) > 1, dco),
    # cos[n], sin[n] ,trigonometricas
    (cos(a*n),
     (z*(z-cos(a)))/(z**2-(2*cos(a))*z+1),
     Abs(a)>0, Abs(z) > 1, dco),
    (sin(a*n),
     (sin(a)*z+0)/(z**2-(2*cos(a))*z+1),
     Abs(a)>0, Abs(z) > 1, dco),
    ]

Fz = None; f_pares = None # sin similar
z_pares_len = len(z_pares) ; i=0
while i<z_pares_len and Fz==None:
    par_nz = z_pares[i]
    n_dom = par_nz[0]
    z_dom = par_nz[1]
    similar = f.match(n_dom)
    # entrega diccionario de expresion similar
    # si el diccionario es vacio, es coincidente
    if similar or similar=={}:
        f_pares = par_nz
        f_args = similar
        Fz_ = z_dom
        Fz  = z_dom.xreplace(similar)
    i = i+1 # siguiente par

# SALIDA
if not(Fz==None):
    print(' se encontró expresión similar:')
    print('  f:           ',f)
    print('  z_pares f[n]:',f_pares[0])
    print('   similar con:',f_args)
    print('  z_pares F[z]:',f_pares[1])
    print('  Fz:         :',Fz)
else:
    print(' NO se encontró una expresión similar...')

El concepto de expresiones similares se prueba con:

f[n] = \mu [n]

donde se observa que a respuesta es un diccionario vacío

 se encontró expresión similar:
  f:            Heaviside(n)
  z_pares f[n]: Heaviside(n)
   similar con: {}
  z_pares F[z]: z/(z - 1)
  Fz:         : z/(z - 1)
>>> 

mientras que para una función que no se encuentra en la tabla, el resultado debe ser Fz=None

f[n] = \sin [2n]
 NO se encontró una expresión similar...
>>> type(Fz)
<class 'NoneType'>
>>> 

El concepto básico del algoritmo se extiende para las tablas de Pares de transformadas que se adjuntan en un archivo de funciones telg1001.py.


Ejercicio 2. Transformada z inversa de F[z] = z/(z-1) con Sympy y la instrucción f.match()

Revisar si existe una expresión para F[z] en una pequeña lista de pares de transformadas:

F[z] = \frac{z}{z-1}

Algoritmo en Python

Semejante al ejercicio 1 para f[n] a F[z], el algoritmo inicia con las variables a usar para cada dominio y de F[z].

Para encontrar semejanzas, se recorre cada par de transformadas, comparando las expresiones con F.match(par_nz[1]), que para revisar la expresión del domino z, solo toma la segunda parte de la tupla. Si existe coincidencia, se crea un diccionario que indica los valores comodín que hacen que las expresiones sean iguales.

Resultado del algoritmo

 se encontró expresión similar:
  F:            z/(z - 1)
  z_pares F[z]: z/(z - 1)
   similar con: {}
  z_pares f[n]: Heaviside(n)
  fn:         : Heaviside(n)
>>> 

Instrucciones en Python

# transformada z inversa con F.match de Sympy
# expresiones similares o semejantes F.match
import sympy as sym

# INGRESO
n = sym.Symbol('n', real=True)
z = sym.Symbol('z')
u = sym.Heaviside(n)

F = z/(z-1)
#F = (z**2 - z*sym.cos(2))/(z**2 - 2*z*sym.cos(2) + 1)

# PROCEDIMIENTO
# para revisar semejanza de expresiones fn y n_dom
a = sym.Wild('a', exclude=[n])
b = sym.Wild('b', exclude=[n])

# tabla de pares [(n_dom, z_dom)]
z_pares = [
    # impulso unitario d[n], DiracDelta
    (DiracDelta(n),
     S.One,
     S.true, S.Zero, dco),
    (DiracDelta(a*n),
     S.One,
     Abs(a)>0, S.Zero, dco),
    # escalon unitario u[n], Heaviside
    (Heaviside(n),
     z/(z-1),
     S.true, Abs(z) > 1, dco),
    # cos[n], sin[n] ,trigonometricas
    (cos(a*n),
     (z*(z-cos(a)))/(z**2-(2*cos(a))*z+1),
     Abs(a)>0, Abs(z) > 1, dco),
    (sin(a*n),
     (sin(a)*z+0)/(z**2-(2*cos(a))*z+1),
     Abs(a)>0, Abs(z) > 1, dco),
    ]

fn = None; F_pares = None # sin similar
z_pares_len = len(z_pares) ; i=0
while i<z_pares_len and fn==None:
    par_nz = z_pares[i]
    n_dom = par_nz[0]
    z_dom = par_nz[1]
    similar = F.match(z_dom)
    # entrega diccionario de expresion similar
    # si el diccionario es vacio, es coincidente
    if similar or similar=={}:
        F_pares = par_nz
        F_args = similar
        fn_ = n_dom
        fn  = n_dom.xreplace(similar)
    i = i+1 # siguiente par

# SALIDA
if not(fn==None):
    print(' se encontró expresión similar:')
    print('  F:           ',F)
    print('  z_pares F[z]:',F_pares[1])
    print('   similar con:',F_args)
    print('  z_pares f[n]:',F_pares[0])
    print('  fn:         :',fn)
else:
    print(' NO se encontró una expresión similar...')

otros ejercicios realizados con

F[z] =\frac{z^2 - zcos(2)}{z^2 - 2zcos(2) + 1}

resultado del algoritmo

 se encontró expresión similar:
  F:            (z**2 - z*cos(2))/(z**2 - 2*z*cos(2) + 1)
  z_pares F[z]: (z**2 - z*cos(a_))/(z**2 - 2*z*cos(a_) + 1)
   similar con: {a_: 2}
  z_pares f[n]: cos(n*a_)
  fn:         : cos(2*n)
>>> 

Referencias: Sympy: match(pattern, old=False) https://docs.sympy.org/latest/modules/core.html?highlight=match#sympy.core.basic.Basic.match

Transformada z – Tabla de Propiedades

Referencia: Schaum Hsu Tabla 4-2 p173. Lathi Tabla 5.2 p509. Oppenheim Tabla 10.3 p793

Propiedad señal Transformada z ROC
x[n]
x1[n]
x2[n]
X[z]
X1[z]
X2[z]
R
R1
R2
Aditiva x1[n] + x2[n] X1[z] + X2[z]
Escalabilidad a x[n] a X[z]
Linealidad a_1 x_1[n] + a_2 x_2[n] a_1 X_1[z] + a_2 X_2[z] R’ ⊃R1∩R2
Desplazamiento
en tiempo
x[n-n0] z-n0 X[z] R’ ⊃ R∩{0<|z|<∞}
Multiplicación
por z0n
z0n x[n] X \big(\frac{z}{z_0}\big) R’ = |z0|R
Multiplicación
por ejβn
ejβn x[n] X(e-jβ z) R’ = R
Inversión en tiempo x[-n] X \big(\frac{1}{z}\big) R’ = 1/R
Multiplicación
por n
n x[n] -z \frac{\delta}{\delta z}X(z) R’ = R
Acumulativa \sum_{k=-\infty}^n x[n] \frac{1}{1-z^{-1}} X(z) R’ ⊃ R∩{|z|>1}
Convolución x_1 \circledast z_2 X1[z]X2[z] R’ ⊃R1∩R2
Valor inicial x[0] \lim _{z \rightarrow \infty} X[z]
Valor Final \lim _{N \rightarrow \infty} x[N] \lim _{z \rightarrow 1} (z-1) X[z]
polos de (z-1)X[z] dentro de círculo unitario

Transformadas z – Tabla

Transformada z -Tabla

Referencia: Lathi Tabla 5.1 Transformada z p492. Oppenheim tabla 10.2 p776, Schaum Hsu Tabla 4-1 p170.

Tabla de Transformada z
No. x[n] X[z] ROC
1a δ[n] 1 Toda z
1b δ[n-m] z-m Toda z excepto
0 (si m>0) ó
∞ (si m<0)
2a μ[n] \frac{z}{z-1} \frac{1}{1-z^{-1}} |z|>1
2b -μ[-n-1] \frac{z}{z-1} \frac{1}{1-z^{-1}} |z|<1
3 n μ[n] \frac{z}{(z-1)^2} \frac{z^{-1}}{(1- z^{-1})^2} |z|>1
4 n2 μ[n] \frac{z(z+1)}{(z-1)^3}
5 n3 μ[n] \frac{z(z^2 + 4z + 1)}{(z-1)^4}
6a γn μ[n] \frac{z}{z-\gamma} \frac{1}{1-\gamma z^{-1}} |z|>|γ|
6b n μ[-n-1] \frac{z}{z-\gamma} \frac{1}{1-\gamma z^{-1}} |z|<|γ|
7 γn-1 μ[n-1] \frac{1}{z-\gamma}
8a n γn μ[n] \frac{\gamma z}{(z-\gamma)^2} \frac{\gamma z^{-1}}{(1- \gamma z^{-1})^2} |z|>|γ|
8b -n γn μ[-n-1] \frac{\gamma z}{(z-\gamma)^2} \frac{\gamma z^{-1}}{(1- \gamma z^{-1})^2} |z|<|γ|
8c (n+1) γn μ[n] \Big[ \frac{z}{z-\gamma}\Big]^2 \frac{1}{(1- \gamma z^{-1})^2} |z|>|γ|
9 n2 γn μ[n] \frac{\gamma z (z + \gamma)}{(z - \gamma)^3 }
10 \frac{n(n-1)(n-2) \text{...} (n-m+1)}{\gamma^m m!}\gamma^n \mu[n] \frac{ z}{(z-\gamma)^{m+1}}
11a |γ|n cos(βn) μ[n] \frac{ z \big(z-|\gamma | \cos (\beta ) \big)}{z^2-(2|\gamma | \cos (\beta ))z +|\gamma |^2} |z|>γ
11b |γ|n sin(βn) μ[n] \frac{ z |\gamma | \sin (\beta )}{z^2-(2|\gamma | \cos (\beta ))z +|\gamma |^2} |z|>γ
12a r|γ|n cos(βn+θ) μ[n] \frac{ rz[z \cos (\theta) - |\gamma | \cos (\beta -\theta)]}{z^2-(2|\gamma | \cos (\beta ))z +|\gamma |^2}
12b r|γ|n cos(βn+θ) μ[n]
γ = |γ| e
\frac{\big(0.5r e^{j \theta} \big)z}{z - \gamma} + \frac{\big(0.5r e^{-j \theta} \big)z}{z - \gamma^{*}}
12c r|γ|n cos(βn+θ) μ[n] \frac{z(Az +B)}{z^2 + 2az + |\gamma|2}
r = \sqrt{\frac{A^2|\gamma |^2 + B^2 - 2AaB}{|\gamma |^2 - a^2}} \beta = \cos ^{-1} \frac{-a}{|\gamma |}

\theta = tan^{-1} \frac{ Aa - B}{A \sqrt{|\gamma |^2 - a^2}}

13 {an ; 0≤ n ≤ N-1
{0  ; otro caso
\frac{1-a^N z^{-n}}{1-az^{-1}} |z|>0

Transformada z – Tabla de propiedades

7.4 LTI DT Transformada z – Ejercicios resueltos con sistemas discretos

2Eva2009TII_T2 LTI DT Dado h[n], y[n] determine x[n]

 

7.3 LTI DT Transformada z – Y[z]=ZIR+ZSR con Sympy-Python

Referencia: Lathi Ejemplo 5.5 p510

continuando con la solución del ejercicio de condiciones iniciales,

y[n+2] – 5 y[n+1] + 6 y[n] = 3 x[n+1] + 5 x[n]

con las condiciones iniciales y[-1]=11/16, y[-2]=37/36,
ante una entrada x[n]=(-2)-nμ[n]

Respuesta
total
= respuesta a
entrada cero
+ respuesta a
estado cero

En el ejemplo se encuentra que la solución total de la ecuación de diferencias se puede separar en dos componentes. El primero es generado por las condiciones iniciales y el segundo por la entrada x[n]

\Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] + \Bigg(-3 + \frac{11}{z} \Bigg) = 3\frac{1}{z-0.5}+5\frac{1}{z(z-0.5)} \Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] = - \Bigg(-3 + \frac{11}{z} \Bigg) + \frac{3z+5}{z(z-0.5)} \Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] = -\text{condiciones iniciales} + \text{entrada x[n]}

para simplificar, se multiplica ambos lados por z2

(z^2 - 5 z + 6 ) Y[z] = - z(-3z +11) + \frac{z(3z+5)}{(z-0.5)} (z^2 - 5 z + 6 ) Y[z] = - \text{condiciones iniciales} + \text{entrada x[n]} Y[z] = \frac{- z(-3z +11)}{(z^2 - 5 z + 6 )} + \frac{z(3z+5)}{(z-0.5)(z^2 - 5 z + 6 ) }

respuesta total = (respuesta a entrada cero) + (respuesta estado cero)

continuando luego con el proceso de fracciones parciales y cambio al dominio de tiempo discreto. (realizado en desarrollo analítico), aqui se usa la transformada_z inversa con Sympy:

y[n] = \Bigg[ \frac{26}{15}(0.5)^n - \frac{7}{3}(2)^n + \frac{18}{5}(3)^n \Bigg] \mu [n]

Yz_ZIR_ZSR_graf01


Instrucciones en Python

Se reutilizan los algoritmos de la sección LTID Transformada z – Fracciones parciales con Python a lo que se añaden las instrucciones de los pasos anteriores.

 Hz {polos:veces} :  {3: 1, 2: 1}
 Hz {ceros:veces} :  {-5/3: 1}

 termino condiciones iniciales: 
z*(11 - 3*z)
termino entrada x[n]:  
2*z*(3*z + 5)
-------------
   2*z - 1   

 Yz = ZIR_z + ZSR_z:
  z*(11 - 3*z)        2*z*(3*z + 5)      
- ------------ + ------------------------
   2                       / 2          \
  z  - 5*z + 6   (2*z - 1)*\z  - 5*z + 6/

 Yz en fracciones parciales z:
    26*z          7*z         18*z  
------------ - --------- + ---------
15*(z - 1/2)   3*(z - 2)   5*(z - 3)

 y[n]:
/      n      n       n\             
|26*0.5    7*2    18*3 |             
|------- - ---- + -----|*Heaviside(n)
\   15      3       5  /             
>>> 

Instrucciones Python

# Transformada z - Fracciones parciales
# Y(z) = -condicion0+entradaxn = ZIR+ZSR
# Lathi Ejemplo 5.5 p510
import sympy as sym
import telg1001 as fcnm
sym.SYMPY_DEBUG=True
# INGRESO
z = sym.Symbol('z')
n = sym.Symbol('n', real=True)

# coeficientes como racional en dominio 'ZZ' enteros
a0 = sym.Rational(1/2).limit_denominator(1000)
# señal de entrada Xz
Xz = z/(z-a0)

# Hz = Pz/Qz
Pz = 3*z+5
Qz = z**2-5*z+6
Hz = Pz/Qz

# condiciones iniciales ascendente ...,y[-2],y[-1]
a1 = sym.Rational(37,36)
a2 = sym.Rational(11,6)
cond_inicio = [a1, a2]

# PROCEDIMIENTO
Fz = sym.simplify(Hz)
# polos y ceros de Fz
[P,Q] = Fz.as_numer_denom()
P = sym.poly(P,z)
Q = sym.poly(Q,z)
P_ceros = sym.roots(P)
Q_polos = sym.roots(Q)

# coeficientes QD
Q_coef  = Q.coeffs()
Q_grado = Q.degree()

# Términos de condiciones iniciales
m0 = len(cond_inicio)
term_0 = 0 
for j in range(0,Q_grado,1):
    term_grado = 0
    for i in range(m0-1-j,m0,1):
        term_cond0 = cond_inicio[i]*(z**((m0-1-j)-i ))
        term_grado = term_grado + term_cond0
    term_0 = term_0 + term_grado*Q_coef[j+1]
    
# salida y(t) a entrada x(t)
term_0  = sym.simplify(term_0*(z**2))
term_xn = sym.simplify(Pz*Xz)
ZIR_z = -sym.simplify(term_0/Q)
ZSR_z = sym.simplify(term_xn/Q)

# Y[z] = entrada0 + estado0
Yz = ZIR_z + ZSR_z

# Y[z] en fracciones parciales y parametros cuadraticos
Yzp = fcnm.apart_z(Yz)
Qs2 = fcnm.Q_cuad_z_parametros(Yzp)

# Inversa de transformada z
yn = 0*n ; Fz_revisar = []
term_sum = sym.Add.make_args(Yzp)
for term_k in term_sum:
    term_kn = fcnm.inverse_z_transform(term_k,z,n)
    if type(term_kn)==tuple:
        yn = yn + term_kn[0]
    else:
        yn = yn + term_kn
yn = yn.collect(sym.Heaviside(n))
yn = yn.collect(sym.DiracDelta(n))
yn = fcnm._round_float_is_int(yn)

# SALIDA
print(' Hz {polos:veces} : ',Q_polos)
print(' Hz {ceros:veces} : ',P_ceros)
print('\n termino condiciones iniciales: ')
sym.pprint(term_0)
print(' termino entrada x[n]:  ')
sym.pprint(term_xn)
print('\n Yz = ZIR_z + ZSR_z:')
sym.pprint(Yz)
print('\n Yz en fracciones parciales z:')
sym.pprint(Yzp)
if len(Qs2)>0:
    print('Y[z] parametros cuadraticos: ')
    for Qs2_k in Qs2:
        print(Qs2_k,':')
        for cadauno in Qs2[Qs2_k].keys():
            print(cadauno,'\t',Qs2[Qs2_k][cadauno])
print('\n y[n]:')
sym.pprint(yn)
if len(Fz_revisar)>0:
    print('\n --- revisar terminos sin transformada en tabla: ---')
    for un_term in Fz_revisar:
        print(un_term)

7.2.1 LTI DT Transformada z – y[n] ecuación lineal de diferencias en z con condiciones iniciales

La transformada z convierte las ecuaciones de diferencias en expresiones algebraicas que permiten encontrar soluciones en el dominio z. A partir de las soluciones en el dominio z, se aplica la transformada inversa z que lleva a la solución en el dominio del tiempo


Ejercicio1

Referencia: Lathi Ejemplo 5.5 p510

Resolver

y[n+2] – 5 y[n+1] + 6 y[n]  = 3 x[n+1] + 5 x[n]

con las condiciones iniciales y[-1]=11/16, y[-2]=37/36,
ante una entrada x[n]=(2)-nμ[n]

Desarrollo analítico

Usando la propiedad de desplazamiento de 2 unidades a la derecha.

y[n] – 5 y[n-1] +6 y[n-2]  = 3 x[n-1] + 5 x[n-2]

se aplica la transformada z, teniendo en cuenta que y[n-k] significa y[n-k]μ[n], pues consideramos solamente la situación de n≥0, y[n] esta presente incluso antes de n=0.

Teniendo así que,

y[n] μ[n] \Leftrightarrow Y[z] y[n-1] μ[n] \Leftrightarrow \frac{1}{z} Y[z] + y[-1] = \frac{1}{z} Y[z] + \frac{11}{6} y[n-1] μ[n] \Leftrightarrow \frac{1}{z} Y[z] + \frac{11}{6} y[n-2] μ[n] \Leftrightarrow \frac{1}{z^2} Y[z] + \frac{1}{z}y[-1] + y[-2] y[n-2] \mu [n] \Leftrightarrow \frac{1}{z^2} Y[z] + \frac{1}{z}\frac{11}{6} +\frac{37}{36}

Conociendo que para una entrada causal x[n]

x[-1] = x[-2] = … = x[-n] = 0

se tiene que:

x[n] = (2)^{-n} \mu [n] = (2^{-1})^n \mu [n] = (0.5)^n \mu [n] \Leftrightarrow \frac{z}{z-0.5} x[n-1] \mu [n] \Leftrightarrow \frac{1}{z}X[z] +x[-1] = \frac{1}{z}\frac{z}{z-0.5} +0= \frac{1}{z-0.5} x[n-2] \mu [n] \Leftrightarrow \frac{1}{z^2}X[z] + \frac{1}{z}x[-1] + x[-2] = = \frac{1}{z^2} \frac{z}{z-0.5} + (0) + (0) = \frac{1}{z(z-0.5)}

en general, para una entrada causal:

x[n-r] \mu [n] \Leftrightarrow \frac{1}{z^r}X[z]

tomando los resultados anteriores y reemplazado en la ecuacion inicial, de tiene

Y[z] - 5 \Bigg[ \frac{1}{z} Y[z] + \frac{11}{6}\Bigg] + 6 \Bigg[\frac{1}{z^2} Y[z] + \frac{1}{z}\frac{11}{6} +\frac{37}{36} \Bigg] = = 3\frac{1}{z-0.5}+5\frac{1}{z(z-0.5)}

reagrupando términos Y[z] y reordenando,

\Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] +\Bigg(-5\frac{11}{6}+ \frac{1}{z}\frac{11}{6}6 +6\frac{37}{36} \Bigg) = = 3\frac{1}{z-0.5}+5\frac{1}{z(z-0.5)} \Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] + \Bigg(-3 + \frac{11}{z} \Bigg) = 3\frac{1}{z-0.5}+5\frac{1}{z(z-0.5)} \Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] = -\Bigg(-3 + \frac{11}{z} \Bigg) + 3\frac{1}{z-0.5}+5\frac{1}{z(z-0.5)}

En el lado derecho se muestran términos generados por una respuesta natural y una respuesta forzada. Dicho de otra forma, se muestran términos generados por las condiciones iniciales y por la señal x[n].

reagrupando el lado derecho en forma de numerador y denominador

\Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] = \frac{3z^2 -9.5z +10.5}{z(z-0.5)}

se puede reescribir, multiplicando cada lado por z2

z^2\Bigg(1 - 5 \frac{1}{z} + 6 \frac{1}{z^2}\Bigg) Y[z] = z^2 \Bigg[\frac{3z^2 -9.5z +10.5}{z(z-0.5)} \Bigg] (z^2 - 5 z + 6) Y[z] = \frac{z(3z^2 -9.5z +10.5)}{(z-0.5)} Y[z] = \frac{z(3z^2 -9.5z +10.5)}{(z-0.5)(z^2 - 5 z + 6)}

se aplica fracciones parciales, usando el algoritmo de la sección Transformada z-fracciones parciales

Y[z] = \frac{26}{15}\frac{z}{z-0.5} - \frac{7}{3}\frac{z}{z-2} + \frac{18}{5}\frac{z}{z-3}

usando la tabla de transformadas z, se obtiene como respuesta en el tiempo discreto

y[n] = \Bigg[ \frac{26}{15}(0.5)^n - \frac{7}{3}(2)^n + \frac{18}{5}(3)^n \Bigg] \mu [n]

Yz_ZIR_ZSR_graf01

7.2 LTI DT Transformada z – X[z] Fracciones parciales modificadas con Python

Referencia: Lathi 5.1-1 p495, Oppenheim 10.3 p757, Hsu 4.5.D p174

Muchas de las transformadas X(z) de interés en la práctica son funciones racionales, que pueden ser expresadas como la suma de fracciones parciales, cuyas transformadas inversas pueden ser encontradas rápidamente en la tabla de transformadas.

Se busca evitar realizar el integral en el plano complejo requerido para encontrar la transformada inversa de z.

El método de las fracciones parciales es práctico porque cada x[n] transformable se define para n≥0, existe  su correspondiente X[z] definida para |z|>r0 y viceversa. (r0 es constante)

Para desarrollar y probar el algoritmo con Sympy-Python, se usará el desarrollo de los tres ejercicios siguientes, con polos únicos, repetidos y complejos. El algoritmo final del literal c integra las soluciones anteriores.


Ejercicio 1. Polos diferentes

Referencia: Lathi Ejercicio 5.3a p495. Hsu. ejercicio 4.29 p198

Realice la expansión en fracciones parciales de,

X[z] = \frac{8z-19}{(z-2)(z-3)}

Se puede encontrar que,

X[z] = \frac{3}{(z-2)}+\frac{5}{(z-3)}

de la tabla de transformadas z se tiene,

x[n] = [3(2)^{n-1} +5(3)^{n-1}] \mu [n-1]

Que tiene una entrada  con términos multiplicadas por μ[n-1], que es un inconveniente y no deseable. Se prefieren las transformadas respecto a μ[n].

Observando la tabla de transformadas z entre los ítems 6a y 7, se tiene que si la señal X[n] es multiplicada por u[n], el numerador tiene un factor z. Esto se consigue expandiendo en fracciones parciales X[z]/z  que son fracciones parciales modificadas cuando se tiene un factor z en el numerador y luego se restauran multiplicando el todo el resultado por z.

\frac{X[z]}{z} = \frac{8z-19}{z(z-2)(z-3)} =\frac{-19/6}{z} + \frac{3/2}{z-2} + \frac{5/3}{z-3}

que al multiplicar ambos lados por z, se obtiene,

X[z] =\frac{-19}{6} + \frac{3}{2}\frac{z}{z-2} + \frac{5}{3}\frac{z}{z-3}

y usando la tabla de transformadas z se obtiene:

x[n] = \frac{-19}{6} \delta [n] + \Big[ \frac{3}{2}(2)^n + \frac{5}{3}(3)^n \Big] \mu[n]

que es el resultado esperado y con respuesta equivalente al resolver con algoritmo iterativo para n=0,1,2,3,…

Por este motivo, es recomendable siempre expandir en fracciones parciales X[z]/z en lugar de solo X[z], pues tiene un factor z en el numerador.

Algoritmo en Python

Para realizar el ejercicio, debemos considerar usar Sympy. Las operaciones se realizan al dividir X[x]/z y simplificar la nueva expresión Xzz, luego una expansión Xzp. El resultado se multiplica término a término por z y de añaden a la expresión total Xzfp.

El bloque de ingreso que se modifica para cada caso es:

Pz = 8*z-19
Qz = (z-2)*(z-3)

El resultado obtenido es:

 Xz:
    8*z - 19   
---------------
(z - 3)*(z - 2)

 Xz/z:
    8*z - 19    
----------------
  / 2          \
z*\z  - 5*z + 6/

 Xz/z.apart:
    3           5        19
--------- + --------- - ---
2*(z - 2)   3*(z - 3)   6*z

 Xz = (Xz/z)*z
   3*z         5*z      19
--------- + --------- - --
2*(z - 2)   3*(z - 3)   6 

>>> 

Instrucciones en Python

# Transformada z- Fracciones parciales
# Polos únicos, repetidos y complejos
# Lathi Ejercicio 5.3a pdf495
# blog.espol.edu.ec/telg1001
import sympy as sym

# INGRESO
z = sym.Symbol('z')

Pz = 8*z-19
Qz = (z-2)*(z-3)
Xz = Pz/Qz

# PROCEDIMIENTO
Fz = sym.simplify(Xz)
# fracciones parciales modificadas
Fzz = (Fz)/z
Fzm = Fzz.apart()
# fracciones parciales restaurada
terminos = Fzm.args
Fzp = 0*z
for untermino in terminos:
    Fzp = Fzp + untermino*z

# SALIDA
print('\n Xz:')
sym.pprint(Xz)
print('\n Xz/z:')
sym.pprint(Fzz)
print('\n Xz/z.apart:')
sym.pprint(Fzm)
print('\n Xz = (Xz/z)*z')
sym.pprint(Fzp)

Ejercicio 2. Polos repetidos

Referencia: Lathi Ejercicio 5.3b p495

Realice la expansión en fracciones parciales de,

X[z] = \frac{z(2z^2-11z+12)}{(z-1)(z-2)^{3}}

Antes de realizar la expansión en fracciones parciales, se divide ambos lados de la expresión para z. Es decir se usa fracciones parciales modificadas

\frac{X[z]}{z} = \frac{1}{z}\frac{z(2z^2-11z+12)}{(z-1)(z-2)^{3}} \frac{X[z]}{z} = \frac{(2z^2-11z+12)}{(z-1)(z-2)^{3}}

donde el modelo de las fracciones parciales a aplicar es:

\frac{(2z^2-11z+12)}{(z-1)(z-2)^{3}} = \frac{k}{z-1} + \frac{a_0}{(z-2)^3} + \frac{a_1}{(z-2)^2} + \frac{a_2}{(z-2)}

Para encontrar las constantes, se evalúa la expresión de la izquierda con los valores de cada raíz del denominador, en cada caso se obvia el término de la raíz en el denominador,

k = \frac{(2z^2-11z+12)}{\cancel{z-1}(z-2)^{3}} \Bigg|_{z=1} = \frac{(2(1)^2-11(1)+12)}{((1)-2)^{3}} = -3 a_0 = \frac{(2z^2-11z+12)}{(z-1)\cancel{(z-2)^{3}}}\Bigg|_{z=2} = \frac{(2(2)^2-11(2)+12)}{((2)-1)} = -2

con lo que la expresión modelo se convierte en:

\frac{(2z^2-11z+12)}{(z-1)(z-2)^{3}} = \frac{-3}{z-1} + \frac{-2}{(z-2)^3} + \frac{a_1}{(z-2)^2} + \frac{a_2}{(z-2)}

Una forma de resolver es por ejemplo para a2, multiplicar ambos lados por z y hacer que z→∞

\frac{(2z^2-11z+12)}{(z-1)(z-2)^{3}} z = z \Big[\frac{-3}{z-1} + \frac{-2}{(z-2)^3} + \frac{a_1}{(z-2)^2} + \frac{a_2}{(z-2)}\Big] \frac{(2z^2-11z+12)}{(z-1)(z-2)^{3}} z = \frac{-3z}{z-1} + \frac{-2z}{(z-2)^3} + \frac{a_1z}{(z-2)^2} + \frac{a_2z}{(z-2)}\Big] 0 = \frac{-3}{1-1/z} + \frac{-2z}{(z-2)^3} + \frac{a_1z}{(z-2)^2} + \frac{a_2}{(1-2/z)}\Big] 0 = -3 + (0) + (0) + a_2 a_2 =3

quedando solamente una incógnita a1 por resolver,

\frac{(2z^2-11z+12)}{(z-1)(z-2)^{3}} = \frac{-3}{z-1} + \frac{-2}{(z-2)^3} + \frac{a_1}{(z-2)^2} + \frac{3}{(z-2)}

El valor de a1 se puede determinar haciendo z tomar un valor conveniente, es decir z=0 en ambos lados de la ecuación

\frac{(2(0)^2-11(0)+12)}{((0)-1)((0)-2)^{3}} = \frac{-3}{(0)-1} + \frac{-2}{((0)-2)^3} + \frac{a_1}{((0)-2)^2} + \frac{3}{((0)-2)} \frac{12}{(-1)(-8)} = \frac{-3}{-1} + \frac{-2}{-8} + \frac{a_1}{4} + \frac{-3}{-2} \frac{3}{2} = 3 + \frac{1}{4} + \frac{a_1}{4} - \frac{3}{2} \frac{6}{2} - \frac{13}{4}= \frac{a_1}{4} -\frac{1}{4}= \frac{a_1}{4} a_1 = -1

completando la expresión:

\frac{X[z]}{z} = \frac{-3}{z-1} + \frac{-2}{(z-2)^3} + \frac{-1}{(z-2)^2} + \frac{3}{(z-2)}

teniendo finalmente X[z] al multiplicar ambos lados por z,

X[z] = \frac{-3z}{z-1} + \frac{-2z}{(z-2)^3} + \frac{-1z}{(z-2)^2} + \frac{3z}{(z-2)}

y usando la tabla de transformadas z se obtiene:

x[n] = \Big[-3 -2 \frac{n(n-1)}{8}(2)^n - \frac{n}{2}(2)^n + 3(2)^n \Big] \mu [n]

simplificando un poco:

x[n] = \Big[-3 -\Big(\frac{n(n-1)}{4} + \frac{n}{2} - 3\Big)(2)^n \Big] \mu [n] x[n] = -\Big[3 +\frac{1}{4}(n^2 + n-12)(2)^n \Big] \mu [n]

Usando el algoritmo en Python anterior, el bloque de ingreso cambia a:

Pz = z*(2*z**2-11*z+12)
Qz = (z-1)*(z-2)**3

con el resultado:

 Xz:
  /   2            \
z*\2*z  - 11*z + 12/
--------------------
         3          
  (z - 2) *(z - 1)  

 Xz/z:
   2            
2*z  - 11*z + 12
----------------
       3        
(z - 2) *(z - 1)

 Xz/z.apart:
    3       3        1          2    
- ----- + ----- - -------- - --------
  z - 1   z - 2          2          3
                  (z - 2)    (z - 2) 

 Xz = (Xz/z)*z
   3*z     3*z       z         2*z   
- ----- + ----- - -------- - --------
  z - 1   z - 2          2          3
                  (z - 2)    (z - 2) 
>>> 

comparando con el resultado analítico es el mismo.


Ejercicio 3. Polos complejos

Referencia: Lathi Ejercicio 5.3c p495

Realice la expansión en fracciones parciales de,

X[z] = \frac{2 z(3z+17)}{(z-1)(z^2 - 6z+25)}

Se realiza la separación en fracciones parciales modificadas

\frac{X[z]}{z} = \frac{2(3z+17)}{(z-1)(z^2 - 6z+25)}

usando el método «cubrir» de Heaviside se tiene que :

k = \frac{2(3z+17)}{\cancel{(z-1)}(z^2 - 6z+25)} \Big|_{z=1}=2

queda por resolver la segunda parte de la fracción.

\frac{X[z]}{z} = \frac{2}{(z-1)}+\frac{Az+B}{z^2 - 6z+25}

Usando el método de los factores cuadráticos, se multiplica ambos lados por z y z→∞

\frac{X[z]}{z}z= z\frac{2}{(z-1)}+z\frac{Az+B}{z^2 - 6z+25} 0 = \frac{2}{(1-1/z)}+\frac{Az^2+Bz}{(z^2 - 6z+25)} 0 = \frac{2}{(1-1/z)}+\frac{A+B\frac{1}{z}}{\frac{1}{z^2}(z^2 - 6z+25)} 0 = \frac{2}{(1-1/z)}+\frac{A+B\frac{1}{z}}{1 - 6\frac{1}{z}+25\frac{1}{z^2}} 0 = 2+A

con lo que A = -2 , para encontrar B se usa un valor conveniente de z=0

\frac{2 z(3z+17)}{(z-1)(z^2 - 6z+25)} = \frac{2}{(z-1)}+\frac{Az+B}{z^2 - 6z+25} \frac{2 (0+17)}{(0-1)(0^2 - 6(0)+25)} = \frac{2}{((0)-1)}+\frac{-2(0)+B}{(0^2 - 6(0)+25)} \frac{34}{-25} = +\frac{2}{-1}+\frac{B}{25} -\frac{34}{25} + 2=\frac{B}{25} -34+ 2(25) = B

con lo que B=16

\frac{X[z]}{z}= \frac{2}{(z-1)}+\frac{-2z+16}{z^2 - 6z+25} X[z]= \frac{2}{(z-1)}+\frac{z(-2z+16)}{z^2 - 6z+25}

con lo que es posible usar la tabla de transformadas z usando los ítems 2a y 12c. Para 12c los valores de A = -2, B = 16, |γ| =5 y a=-3.

r = \sqrt{\frac{(-2)^2 (5)^2+(16)^2-2(-2)(-3)(16)}{(5^2 -(-3)^2}} = \sqrt{\frac{100+256-192}{25-9}} =3.2

\beta = \cos^{-1} \Big(\frac{-(-3)}{5} \Big) = 0.927 \theta = \tan^{-1} \Bigg(\frac{(-2)(-3)-16}{(-2)\sqrt{(5^2 -(-3)^2}}\Bigg) = \tan^{-1}\Big( \frac{-10}{-8}\Big) = -2.246

reemplazando en la transformada, se encuentra x[n].

x[n] = [2+3.2(5)^n \cos(0.927n-2.246)] \mu [n]

pasamos a probar el algoritmo, donde se encuentra que para el denominador hay raíces complejas. Otra forma de observar es que las funciones parciales aún entregan resultados con términos que tienen el denominador con grado 2. Donde hay que usar expresiones de la tabla de transformadas.

 Xz:
     2*z*(3*z + 17)    
-----------------------
        / 2           \
(z - 1)*\z  - 6*z + 25/

 Xz en fracciones parciales
   2*z*(z - 8)     2*z 
- ------------- + -----
   2              z - 1
  z  - 6*z + 25        
parametros cuadraticos: 
-2*z*(z - 8)/(z**2 - 6*z + 25) :
r 	 3.2015621187164243
gamma 	 5.0
beta 	 0.9272952180016123
theta 	 0.8960553845713439
>>>  

Instrucciones en Python

El algoritmo inicia de la misma forma que en la sección anterior. Ahora hay que revisar el grado del denominador en cada término. Si es de grado 2, se calculan los valores de r, β y θ para armar las transformada a partir de la tabla.

# Transformada z- Fracciones parciales
# Polos únicos, repetidos y complejos
# Lathi Ejercicio 5.3a pdf495
# blog.espol.edu.ec/telg1001
import numpy as np
import sympy as sym

# INGRESO
z = sym.Symbol('z')

Pz = 2*z*(3*z+17)
Qz = (z-1)*(z**2-6*z+25)

#Pz = z*(2*z**2-11*z+12)
#Qz = (z-1)*(z-2)**3

##Pz = 8*z-19
##Qz = (z-2)*(z-3)
Xz = Pz/Qz

# PROCEDIMIENTO
def apart_z(Fz):
    ''' fracciones parciales en dominio z
        modifica con factor 1/z
    '''
    Fz = sym.simplify(Fz)
    # fracciones parciales modificadas con 1/z
    Fzz = (Fz)/z
    Fzm = sym.apart(Fzz,z)
    # restaura z
    term_suma = sym.Add.make_args(Fzm)
    Fzp = 0*z
    for term_k in term_suma:
        Fzp = Fzp + term_k*z
    return(Fzp)

def Q_cuad_z_parametros(Fz):
    ''' parametros cuadraticos en dominio z
    '''

    def Q_cuad_z_term(untermino):
        ''' parametros cuadraticos en dominio z
            de un termino de fraccin parcial
        '''
        unparametro ={}
        # revisa denominador cuadratico
        [numerador,denominador] = (untermino).as_numer_denom()
        gradoD = 0
        coeficientesD = denominador
        gradoN = 0
        coeficientesN = numerador
        if not(denominador.is_constant()):
            denominador = denominador.as_poly()
            gradoD = denominador.degree()
            coeficientesD = denominador.coeffs()
        if not(numerador.is_constant()):
            numerador = numerador.as_poly()
            gradoN = numerador.degree()
            coeficientesN = numerador.coeffs()
        if gradoD == 2 and gradoN==2:
            a = float(coeficientesD[1])/2
            gamma2 = float(coeficientesD[2])
            gamma = np.sqrt(gamma2)
            A = float(coeficientesN[0])
            B = float(coeficientesN[1])
            rN = (A**2)*gamma2 + B**2 - 2*A*a*B
            rD = gamma2 - a**2
            r = np.sqrt(rN/rD)
            beta = np.arccos(-a/gamma)
            thetaN = A*a-B
            thetaD = A*np.sqrt(gamma2-a**2)
            theta = np.arctan(thetaN/thetaD)
            unparametro = {'r':r,
                           'gamma':gamma,
                           'beta':beta,
                           'theta':theta}
        return(unparametro)

    Fz = apart_z(Fz)
    # parametros denominador cuadratico
    Qs2 = {}
    term_suma = sym.Add.make_args(Fz)
    for term_k in term_suma:
        Qs2_k = Q_cuad_z_term(term_k)
        if len(Qs2_k)>0:
            Qs2[term_k] = Qs2_k
    return(Qs2)

Fz  = apart_z(Xz)
Qs2 = Q_cuad_z_parametros(Fz)

# SALIDA
print('\n Xz:')
sym.pprint(Xz)
print('\n Xz en fracciones parciales')
sym.pprint(Fz)
if len(Qs2)>0:
    print('parametros cuadraticos: ')
    for Qs2_k in Qs2:
        print(Qs2_k,':')
        for cadauno in Qs2[Qs2_k].keys():
            print(cadauno,'\t',Qs2[Qs2_k][cadauno])