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])

7.1.5 Transformada z Inversa con Sympy-Python

La Transformada z  inversa, usa la Tabla de Pares f[n] y F[z] y los algoritmos para buscar una expresión semejante F.match() en conjunto con la tabla de propiedades y los algoritmos desarrollados en la sección anterior. El resultado se integra desde telg1001.py y se muestra en los siguientes ejemplos:

Ejemplo 1.  Transformada z inversa de una fracción X[1/z]

Referencia: Oppenheim Ejemplo 10.13 p761, Lathi Ejemplo 5.1 p490

Continuando con el ejercicio presentado para convertir x[n] a X[z], se tiene que:

X[z] = \frac{1}{1-a z^{-1}}\text{ , } |z|>|a| X[z] = \frac{z}{z-a}\text{ , } \Big|z|>|a|

siendo la expresión de ingreso al algoritmo:

F = z/(z - 2)

el resultado es:

f[n] : (2**n*Heaviside(n), Abs(z) > 2, True)
>>> 

Ejemplo 1a. Instrucciones Python

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

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

# (a**n)*f[n] <--> F(z/a)
F = z/(z - 2)  #f = (2**n)*u

# PROCEDIMIENTO
fn = fcnm.inverse_z_transform(F,z,n)

# SALIDA
print('f[n] :',fn)

El algoritmo presentado puede ser tomado como punto de partida para los siguientes ejercicios. Realizado para explicación conceptual.

Ejemplo 2. Transformada z inversa usando expansión en fracciones parciales

Referencia: Lathi 5.3 a

Encuentre la transformada z inversa para:

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

el ingreso para el algoritmo del ejercicio anterior es:

F = (8*z-19)/((z-2)*(z-3))

con el resultado siguiente:

f[n] : (9*2**n + 10*3**n)*Heaviside(n)/6 - 19*DiracDelta(n)/6

activando la variable sym.SYMPY_DEBUG=True, se observa el proceso de fracción parcial modificada, la búsqueda en la tabla, y aplicación de una propiedad de la transformada z.

inverse_z_transform(F,z,n): (8*z - 19)/((z - 3)*(z - 2))

apart_z(F) : (8*z - 19)/((z - 3)*(z - 2))
_simplify_z: (8*z - 19)/((z - 3)*(z - 2))
  P_leadcoef: 8  ; P_zc: 1  ; P: z - 19/8
  _simplify_z k_sign: 1  ; k: 8  ; P_zc: 1  ; Q_zc: 1
    F[z]: (z - 19/8)/((z - 3)*(z - 2))
_simplify_z: (z - 19/8)/(z*(z - 3)*(z - 2))
  P_leadcoef: 8  ; P_zc: 1  ; P: z - 19/8
  Q_leadcoef: 8  ; Q_zc: z  ; Q: (z - 3)*(z - 2)
  _simplify_z k_sign: 1  ; k: 1  ; P_zc: 1  ; Q_zc: z
    F[z]: (z - 19/8)/((z - 3)*(z - 2))
  P: (8*z - 19)/8 
  Q: (z - 3)*(z - 2) 
  P_degree: 1  ; P_zeros: {19/8: 1} 
  Q_degree: 3  ; Q_poles: {3: 1, 2: 1, 0: 1}
  apart(F/z): 3/(16*(z - 2)) + 5/(24*(z - 3)) - 19/(48*z)
  ma_Qk: {a_: 48, b_: 0, c_: 1}  ; ma_Qk2: {b_: 48, a_: 0, c_: 0}
_simplify_z: -19/(48*z)
  P_leadcoef: -19  ; P_zc: 1  ; P: 1
  Q_leadcoef: 48  ; Q_zc: z  ; Q: 1
  _simplify_z k_sign: -1  ; k: 19/48  ; P_zc: 1  ; Q_zc: z
    F[z]: 1
  ma_Qk: {a_: 16, b_: -32, c_: 1}  ; ma_Qk2: {b_: 16, c_: -32, a_: 0}
_simplify_z: 3/(16*(z - 2))
  P_leadcoef: 3  ; P_zc: 1  ; P: 1
  Q_leadcoef: 16  ; Q_zc: 1  ; Q: z - 2
  _simplify_z k_sign: 1  ; k: 3/16  ; P_zc: 1  ; Q_zc: 1
    F[z]: 1/(z - 2)
  ma_Qk: {a_: 24, b_: -72, c_: 1}  ; ma_Qk2: {b_: 24, c_: -72, a_: 0}
_simplify_z: 5/(24*(z - 3))
  P_leadcoef: 5  ; P_zc: 1  ; P: 1
  Q_leadcoef: 24  ; Q_zc: 1  ; Q: z - 3
  _simplify_z k_sign: 1  ; k: 5/24  ; P_zc: 1  ; Q_zc: 1
    F[z]: 1/(z - 3)

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

 apart_z(F) as partial fractions: (-19/6, 3*z/(2*(z - 2)), 5*z/(3*(z - 3)))
  term_k[z]: -19/6
[0] _z_pairs_prop_inverse ----
_simplify_z: -19/6
  P_leadcoef: -19  ; P_zc: 1  ; P: 1
  Q_leadcoef: 6  ; Q_zc: 1  ; Q: 1
  _simplify_z k_sign: -1  ; k: 19/6  ; P_zc: 1  ; Q_zc: 1
    F[z]: 1
 _z_pairs_table match: 
  F: 1
  z_pair F[z]: 1  ; ma_z: {}
  z_pair f[n]: DiracDelta(n)
  try,check  : True -> True
[0]   pair f[n]: (-19*DiracDelta(n)/6, True, True)
  term_k[n]: (-19*DiracDelta(n)/6, True, True)
  term_k[z]: 3*z/(2*(z - 2))
[0] _z_pairs_prop_inverse ----
_simplify_z: 3*z/(2*(z - 2))
  P_leadcoef: 3  ; P_zc: 1  ; P: z
  Q_leadcoef: 2  ; Q_zc: 1  ; Q: z - 2
  _simplify_z k_sign: 1  ; k: 3/2  ; P_zc: 1  ; Q_zc: 1
    F[z]: z/(z - 2)

[1] z_properties_inverse_z_transform
  P: z 
  Q: z - 2 
  P_degree: 1  ; P_zeros: {0: 1} 
  Q_degree: 1  ; Q_poles: {2: 1}
  ma_P1 (a*z+ b):    {a_: 1, b_: 0}
  ma_Q1 (a*z-b)**c : {a_: 1, b_: 2, c_: 1}

 _z_property multiply (a**n)*f[n] <--> F(z/a) 
 k_sign: 1 ; k: 3/2  ; k_b: 2
 F[z]: z/(z - 1)
[2] _z_pairs_prop_inverse ----
_simplify_z: z/(z - 1)
    F[z]: z/(z - 1)
 _z_pairs_table match: 
  F: z/(z - 1)
  z_pair F[z]: z/(z - 1)  ; ma_z: {}
  z_pair f[n]: Heaviside(n)
  try,check  : True -> True
[2]   pair f[n]: (Heaviside(n), Abs(z) > 1, True)

  _z_property multiply (a**n)*f[n]:
  (3*2**n*Heaviside(n)/2, Abs(z) > 2, True)
[1]  _z_properties_inverse f[n]: (3*2**n*Heaviside(n)/2, Abs(z) > 2, True)
[0]   prop f[n]: (3*2**n*Heaviside(n)/2, Abs(z) > 2, True)
  term_k[n]: (3*2**n*Heaviside(n)/2, Abs(z) > 2, True)
  term_k[z]: 5*z/(3*(z - 3))
[0] _z_pairs_prop_inverse ----
_simplify_z: 5*z/(3*(z - 3))
  P_leadcoef: 5  ; P_zc: 1  ; P: z
  Q_leadcoef: 3  ; Q_zc: 1  ; Q: z - 3
  _simplify_z k_sign: 1  ; k: 5/3  ; P_zc: 1  ; Q_zc: 1
    F[z]: z/(z - 3)

[1] z_properties_inverse_z_transform
  P: z 
  Q: z - 3 
  P_degree: 1  ; P_zeros: {0: 1} 
  Q_degree: 1  ; Q_poles: {3: 1}
  ma_P1 (a*z+ b):    {a_: 1, b_: 0}
  ma_Q1 (a*z-b)**c : {a_: 1, b_: 3, c_: 1}

 _z_property multiply (a**n)*f[n] <--> F(z/a) 
 k_sign: 1 ; k: 5/3  ; k_b: 3
 F[z]: z/(z - 1)
[2] _z_pairs_prop_inverse ----
_simplify_z: z/(z - 1)
    F[z]: z/(z - 1)
 _z_pairs_table match: 
  F: z/(z - 1)
  z_pair F[z]: z/(z - 1)  ; ma_z: {}
  z_pair f[n]: Heaviside(n)
  try,check  : True -> True
[2]   pair f[n]: (Heaviside(n), Abs(z) > 1, True)

  _z_property multiply (a**n)*f[n]:
  (5*3**n*Heaviside(n)/3, Abs(z) > 3, True)
[1]  _z_properties_inverse f[n]: (5*3**n*Heaviside(n)/3, Abs(z) > 3, True)
[0]   prop f[n]: (5*3**n*Heaviside(n)/3, Abs(z) > 3, True)
  term_k[n]: (5*3**n*Heaviside(n)/3, Abs(z) > 3, True)

 f[n]: 3*2**n*Heaviside(n)/2 + 5*3**n*Heaviside(n)/3 - 19*DiracDelta(n)/6
 f[n]: (9*2**n + 10*3**n)*Heaviside(n)/6 - 19*DiracDelta(n)/6
f[n] : (9*2**n + 10*3**n)*Heaviside(n)/6 - 19*DiracDelta(n)/6
>>> 

7.1 Transformada z – Sumatoria con Sympy-Python

Referencia: Lathi 5.1 p488, Oppenheim 10.1 p741

Se define como X[z] a la transformada z de una señal x[n] como

X[z] = \sum_{n=-\infty}^{\infty} x[n] z^{-n}

Por razones semejantes a las descritas en la unidad 4 para transformadas de Laplace, es conveniente considerar la transformada z unilateral. Dado que muchos de los sistemas y señales son causales, en la práctica se considera que las señales inician en n=0 (señal causal), la definición de la transformada z unilateral es la misma que la bilateral. excepto por los limites de la sumatoria que son [0,∞]

X[z] = \sum_{n=0}^{\infty} x[n] z^{-n}

Ejemplo 1. Transformada z de exponencial causal

Referencia: Lathi ejemplo 5.1 p490, Oppenheim ejemplo 10.1 p743

Encuentre la Transformada z y la correspondiente ROC para la señal x[n]

x[n] = a ^{n} \mu[n]

Transformada Z Ej01x[n]

1.1 Desarrollo analítico

por definición

X[z] = \sum_{n=0}^{\infty} a ^{n} \mu[n] z^{-n}

dado que μ[n]=1 para todo n≥0,

X[z] = \sum_{n=0}^{\infty} \Big( a^n \Big) \Big(\frac{1}{z} \Big)^{n} = \sum_{n=0}^{\infty} \Big(\frac{a}{z} \Big)^{n} =1 + \Big(\frac{a}{z} \Big)^{1}+\Big(\frac{a}{z} \Big)^{2}+\Big(\frac{a}{z} \Big)^{3}+\text{...}

revisando la progresión geométrica,

1+x+x^2+x^3+\text{...} = \frac{1}{1+x} \text{ , }|x|<1

y aplicando en la expresión del problema, se tiene:

X[z] = \frac{1}{1-\frac{a}{z}}\text{ , } \Big|\frac{a}{z}\Big| <1 X[z] = \frac{z}{z-a}\text{ , } |z|>|a|

observe que X[z] existe solo si |z|>|a|. Para |z|<|a| la sumatoria no converge y tiende al infinito. Por lo que, la Región de Convergencia ROC de X[z] es la región sombreada de un círculo de radio |a| centrado en el origen en el plano z.

Transformada Z Ej01 ROC
Luego se puede mostrar que la transformada z de la señal -an u[-(n+1)] también es z/(z-a). Sin embargo la región de convergencia en éste caso es |z|<|a|. Se observa que la transformada z inversa no es única, sin embargo,al restringir la transformada inversa a causal, entonces la transformada inversa es única como la mostrada.

Transformada Z Ej01 Xz

1.2 Transformada z con Sympy-Python

En Sympy de Python existe la función sym.summation(), que se usa para generar la expresión del resultado para la transformada z.

f[n]: 
 n
a 

 F[z] desde sumatoria:
    1     |a|     
(-------, |-| < 1)
   a      |z|     
 - - + 1          
   z              

 F[z] simplificada
  z   
------
-a + z

 ROC: 
|a|    
|-| < 1 
|z| 

{Q_polos:veces}: {a: 1} 
{P_ceros:veces}: {0: 1} 
>>> 

Instrucciones en Python

# transformada z de x[n]u[n]
import sympy as sym

# INGRESO
z = sym.symbols('z')
n = sym.symbols('n', positive=True)
a = sym.symbols('a')
u = sym.Heaviside(n)

fn = (a**n)*u

# valor a como racional en dominio 'ZZ' enteros
a_k = sym.Rational(1/2).limit_denominator(100)
m   = 7        # Términos a graficar
muestras = 101 # dominio z

# PROCEDIMIENTO
fnz = fn*(z**(-n)) # f(n,z) para sumatoria
# sumatoria transformada z
Fz_sum = sym.summation(fnz,(n,0,sym.oo))
Fz_eq  = Fz_sum.args[0]  # primera ecuacion e intervalo
Fz = Fz_eq[0].simplify() # solo expresion

ROC = Fz_eq[1]  # condicion ROC

# 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)

# SALIDA
print('f[n]: ')
sym.pprint(fn)
print('\n F[z] desde sumatoria:')
sym.pprint(Fz_eq)
print('\n F[z] simplificada')
sym.pprint(Fz)
print('\n ROC: ')
sym.pprint(ROC)
print('\n {Q_polos:veces}:',Q_polos)
print(' {P_ceros:veces}:',P_ceros)

La parte gráfica se desarrolla reutilizando algunas funciones de los algoritmos telg1001.py

Ser requiere dar valor a la constante ‘a‘ y se sustituye en las funciones con a_k=1/2. Para actualizar los polos, se usa la función buscapolos() realizada en la unidad 4.3.2, añadiendo en la función la sustitución en F[z] de variable independiente a F(s) para que sea transparente el cálculo. Lo mismo se aplica a la grafica de la transformada F[z] y los resultados son los mostrados.

# GRAFICA  -----------
import numpy as np
import matplotlib.pyplot as plt
import telg1001 as fcnm

def graficar_Fz_polos(Fz,Q_polos={},P_ceros={},
                z_a=1,z_b=0,muestras=101,f_nombre='F',
                solopolos=False):
    ''' polos y ceros plano z imaginario
    '''
    fig_zROC, graf_ROC = plt.subplots()
    # limite con radio 1
    radio1 = plt.Circle((0,0),1,color='lightsalmon',
                        fill=True)
    radio2 = plt.Circle((0,0),1,linestyle='dashed',
                        color='red',fill=False)
    graf_ROC.add_patch(radio1)
    for unpolo in Q_polos.keys():
        [r_real,r_imag] = unpolo.as_real_imag()
        unpolo_radio = np.abs(unpolo)
        unpolo_ROC = plt.Circle((0,0),unpolo_radio,
                          color='lightgreen',fill=True)
        graf_ROC.add_patch(unpolo_ROC)
    graf_ROC.add_patch(radio2) # borde r=1
    graf_ROC.axis('equal')
    # marcas de r=1 y polos
    for unpolo in Q_polos.keys():
        x_polo = sym.re(unpolo)
        y_polo = sym.im(unpolo)
        etiqueta = 'polo: '+str(unpolo)
        graf_ROC.scatter(x_polo,y_polo,marker='x',
                        color='red',label = etiqueta)
        etiqueta = "("+str(float(x_polo)) + ','
        etiqueta = etiqueta + str(float(y_polo))+")"
        plt.annotate(etiqueta,(x_polo,y_polo), rotation=45)
    # marcas de ceros
    for uncero in P_ceros.keys():
        x_cero = sym.re(uncero)
        y_cero = sym.im(uncero)
        etiqueta = 'cero: '+str(uncero)
        graf_ROC.scatter(x_cero,y_cero,marker='o',
                        color='blue',label = etiqueta)
        etiqueta = "("+str(float(x_cero)) + ','
        etiqueta = etiqueta + str(float(y_cero))+")"
        plt.annotate(etiqueta,(x_cero,y_cero), rotation=45)
    # limita radio 1
    graf_ROC.plot(1,0,'o',color='red',
                 label ='radio:'+str(1))
    graf_ROC.axhline(0,color='grey')
    graf_ROC.axvline(0,color='grey')
    graf_ROC.grid()
    graf_ROC.legend()
    graf_ROC.set_xlabel('Re[z]')
    graf_ROC.set_ylabel('Imag[z]')
    untitulo = r'ROC '+f_nombre+'[z]=$'
    untitulo = untitulo+str(sym.latex(Fz))+'$'
    graf_ROC.set_title(untitulo)
    return(fig_zROC)

# a tiene valor a_k
fn = fn.subs(a,a_k) 
Fz = Fz.subs(a,a_k)
# polos y ceros de Fz
polosceros = fcnm.busca_polosceros(Fz)
Q_polos = polosceros['Q_polos']
P_ceros = polosceros['P_ceros']
# estima intervalo para z
z_a = list(Q_polos.keys())
z_a.append(1) # comparar con radio 1
z_a = 2*int(max(z_a))

fig_ROC = graficar_Fz_polos(Fz,Q_polos,P_ceros,
                      muestras=101,f_nombre='X')

fig_Fz = fcnm.graficar_Fs(Fz,Q_polos,P_ceros,
                     -z_a,z_a,muestras=101,
                     f_nombre='X')

# para graficar f[n]
f_n = sym.lambdify(n,fn)
ki  = np.arange(0,m,1)
fi  = f_n(ki)

# entrada x[n]
fig_fn, grafxn = plt.subplots()
plt.axvline(0,color='grey')
plt.stem(ki,fi)
plt.grid()
plt.xlabel('n')
plt.ylabel('x[n]')
etiqueta = r'x[n]= $'+str(sym.latex(fn))+'$'
plt.title(etiqueta)

plt.show()

Referencia: The z-transform.dynamics-and-control. https://dynamics-and-control.readthedocs.io/en/latest/1_Dynamics/9_Sampled_systems/The%20z%20transform.html

6.5 LTI DT – Convolución de sumas – Python

Referencia: Lathi 3.8 p282, Oppenheim 2.1.2 p77 pdf108, Hsu 2.6.C p62

Una señal discreta de entrada x[n] se representa como una superposición de versiones escaladas de un conjunto de impulsos unitarios desplazados δ[n-k], cada uno con valor diferente de cero en un solo punto en el tiempo, especificado por el valor de k.

La respuesta de un sistema lineal y[n] a x[n] es la superposición de las respuestas escaladas del sistema a cada uno de estos impulsos desplazados.

Si usamos hk[n] como la respuesta del sistema lineal al impulso unitario desplazado por δ[n-k]. La respuesta y[n] del sistema lineal a la entrada x[n] en la ecuacion será la combinación lineal ponderada de las respuesta básicas.

y[n] = \sum_{k=-\infty}^{+\infty} x[k]h_{k}[n]

Si se conoce la respuesta de un sistema lineal al conjunto de impulsos unitarios desplazados, podemos construir una respuesta a una entrada arbitraria.

Si el sistema lineal también es invariante en el tiempo, entonces estas respuestas a impulsos unitarios desplazados en el tiempo son todas las versiones desplazadas en el tiempo unas de otras.

h[n] es la salida del sistema LTI cuando δ[n] es la entrada. Entonces para un sistema LTI la ecuación se vuelve.

y[n] = \sum_{k=-\infty}^{+\infty} x[k]h[n-k]

El resultado se conoce como la «convolución de suma» o «suma de superposición» y la operación miembro derecho de la ecuación se llama convolución de las secuencias x[n] y h[n] que se representa de manera simbólica como:

y[n] = x[n]*h[n]

Ejemplo

Referencia: Openheim Ejemplo 2.4 p85 pdf116

Considere una entrada x[n] y una respuesta al impulso unitario h[n] dada por:

x[n] = (u[n]-u[n-5])
h[n] = αn (u[n]-u[n-7])

con α>1.  Para el ejercicio, α=1.5.

Nota: Para el algoritmo, si α es un entero, por ejemplo 2, usar α=2.0, para que la operación potencia se realice con números reales, como entero se puede saturar y dar error.


Al aplicar la suma de convolución obtiene:

Para aplicar el algoritmo se requiere definir u[n], por ser parte de las funciones x[n] y h[n]. Dado que la operación requiere valores fuera del rango muestreado para n, la sección suma convolución utiliza las funciones en lugar de los vectores xi, hi.

La función está definida en un intervalo simétrico, por lo que el rango de trabajo [a,b] se mantiene de la forma [-b,b] en las instrucciones.

# Respuesta a impulsos - forma discreta
# Ejemplo Oppenheim Ejemplo 2.3 p83/pdf111
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
# Rango [a,b], simétrico a 0
b = 15 ; a = -b

alfa = 1.5
u = lambda n: np.piecewise(n,n>=0,[1,0])

x = lambda n: u(n)-u(n-5)
h = lambda n: (alfa**n)*(u(n)-u(n-7))


# PROCEDIMIENTO
ni = np.arange(a,b+1,1)
xi = x(ni)
hi = h(ni)

# Suma de Convolucion x[n]*h[n]
muestras = len(xi)
yi = np.zeros(muestras, dtype=float)
for i in range(0,muestras):
    suma = 0
    for k in range(0,muestras):
        suma = suma + x(ni[k])*h(ni[i]-ni[k])
    yi[i] = suma

# yi = np.convolve(xi,hi,'same')

# SALIDA - GRAFICA
plt.figure(1)
plt.suptitle('Suma de Convolución x[n]*h[n]')

plt.subplot(311)
plt.stem(ni,xi, linefmt='b--',
         markerfmt='bo',basefmt='k-')
plt.ylabel('x[n]')

plt.subplot(312)
plt.stem(ni,hi, linefmt='b--',
         markerfmt='ro',basefmt='k-')
plt.ylabel('h[n]')

plt.subplot(313)
plt.stem(ni,yi, linefmt='g-.',
         markerfmt='mo', basefmt='k-')

plt.ylabel('x[n]*h[n]')
plt.xlabel('n')

plt.show()

La suma convolución se encuentra también disponible con Numpy en np.convolve(), la  sección de suma convolución se puede reemplazar y obtener los mismos resultados. Considere que para éste caso se usan los vectores xi y hi.

yi = np.convolve(xi,hi,'same')

el algoritmo se puede aplicar a otros ejercicios para comprobar los resultados.

Continúe con el ejercicio 2.5 del libro.

6.4.1 LTI DT – Respuesta estado cero. Ejercicio con Python

Referencia: Lathi Ejemplo 3.21 p286

Determine la respuesta a estado cero de un sistema LTID descrito por la ecuación de diferencias mostrada,

y[n+2] - 0.6 y[n+1] - 0.16 y[n] = 5x[n+2]

Si la entrada es x[n] = 4-n u[n]


Para realizar el diagrama, se desplaza la ecuación en dos unidades

y[n] - 0.6 y[n-1] - 0.16 y[n-2] = 5x[n] y[n] = 0.6 y[n-1] + 0.16 y[n-2] + 5x[n]

las condiciones iniciales es estado cero son todas cero.


La entrada puede expresada como:

x[n] = 4^{-n} \mu [n] = \Big( \frac{1}{4} \Big)^n \mu [n] x[n] = 0.25^{n} \mu [n]

la respuesta al impulso del sistema se encontró en el ejemplo de la sección anterior:

h[n] = [ (-0.2)^n +4 (0.8)^n ] \mu [n]

Por lo que:

y[n] = x[n] \circledast h[n] = 0.25^{n} \mu [n] \circledast \Big[ (-0.2)^n +4 (0.8)^n \Big] \mu [n] = 0.25^{n} \mu [n] \circledast (-0.2)^n \mu [n] + 0.25^{n} \mu [n] \circledast 4 (0.8)^n \mu [n]

usando la tabla de convolución de sumas para tener:

y[n] = \Bigg[ \frac{0.25^{n+1}-(-0.2)^{n+1}}{0.25-(-0.2)} + 4 \frac{0.25^{n+1} - 0.8^{n+1}}{0.25-0.8}\Bigg] \mu [n] = \Bigg[2.22 \Big[ 0.25^{n+1} - (-0.2)^{n+1}\Big] + - 7.27 \Big[ 0.25^{n+1} - (0.8)^{n+1}\Big]\Bigg] \mu [n] = \Big[ -5.05 (0.25)^{n+1} - 2.22(-0.2)^{n+1} + 7.27 (0.8)^{n+1} \Big] \mu [n]

recordando que ϒn+1 = ϒ(ϒ)n se puede expresar,

y[n] = \Big[-1.26 (0.25)^{n} + 0.444 (-0.2)^{n} +5.81(0.8)^{n}\Big] \mu [n]

Desarrollo numérico con Python

La respuesta a estado cero se encuentra en forma numérica con la expresión:

y[n] = x[n] \circledast h[n]

que en Numpy es np.convolve() y facilita el cálculo de la convolucion de sumas. El tema de convolución con Python se desarrolla en la siguiente sección.

La respuesta del algoritmo se presenta en la gráfica:

En el algoritmo se compara la solución numérica yi[n] con la solución analítica yia[n], obteniendo un error del orden 10-3 dado por el truncamiento de dígitos en ambos métodos.

 error numerico vs analítico: 
 maximo de |error|:  0.006000000000000227
 errado: 
[-0.          0.         -0.          0.         -0.          0.
 -0.          0.         -0.          0.         -0.         -0.
 -0.          0.          0.         -0.006      -0.0058     -0.00509
 -0.0041445  -0.00334173 -0.00267831 -0.0021442  -0.00171569 -0.00137264
 -0.00109813 -0.00087851 -0.00070281 -0.00056225 -0.0004498  -0.00035984
 -0.00028787]

El algoritmo continúa como un anexo a los algoritmos de respuesta a entrada cero e impulso.

Instrucciones en Python

# Sistema LTID. Respuesta entrada cero, 
# Respuesta a impulso
# resultado con raices Reales
# ejercicio lathi ejemplo 3.121 p286
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt

# INGRESO
# coeficientes E con grado descendente
Q = [1., -0.6, -0.16]
P = [5.,  0,    0   ]
# condiciones iniciales con n ascendente -2,-1, 0
inicial = [25/4, 0.] 

# PROCEDIMIENTO
# raices
gamma = np.roots(Q)

# revisa raices numeros complejos
revisaImag = np.iscomplex(gamma)
escomplejo = np.sum(revisaImag)
if escomplejo == 0:
    
    # coeficientes de ecuacion
    m = len(Q)-1
    A = np.zeros(shape=(m,m),dtype=float)
    for i in range(0,m,1):
        for j in range(0,m,1):
            A[i,j] = gamma[j]**(-m+i)
    ci = np.linalg.solve(A,inicial)

    # ecuacion yn
    n = sym.Symbol('n')
    y0 = 0
    for i in range(0,m,1):
        y0 = y0 + ci[i]*(gamma[i]**n)

    # Respuesta impulso
    ni = np.arange(-m,m,1)
    hi = np.zeros(m, dtype=float)
    xi = np.zeros(2*m, dtype=float)
    xi[m] = 1 # impulso en n=0

    # h[n] iterativo
    p_n = len(P)
    for i in range(0,m,1):
        for k in range(m,2*m,1):
            hi[i] = hi[i] - Q[k-m]*hi[m-k+1]
        for k in range(0,p_n,1):
            hi[i] = hi[i] + P[k]*xi[m+i]
    Bh = hi[:m]

    # coeficientes de ecuacion
    m = len(Q)-1
    Ah = np.zeros(shape=(m,m),dtype=float)
    for i in range(0,m,1):
        for j in range(0,m,1):
            Ah[i,j] = gamma[j]**(i)
    ch = np.linalg.solve(Ah,Bh)

    # ecuacion hn
    hn = 0
    for i in range(0,m,1):
        hn = hn + ch[i]*(gamma[i]**n)

    # Respuesta de estado cero ----------------
    # Rango [a,b], simetrico a 0
    b = 15 ; a = -b
    
    u = lambda n: np.piecewise(n,[n>=0,n<0],[1,0])
    
    x = lambda n: (0.25)**(n)*u(n)
    hL = sym.lambdify(n,hn,'numpy')
    h = lambda n: hL(n)*u(n)
    
    # PROCEDIMIENTO
    ni = np.arange(a,b+1,1)
    xi = x(ni)
    hi = h(ni)
    
    # Suma de Convolucion x[n]*h[n]
    yi = np.convolve(xi,hi,'same')
    
    # compara respuesta numerica con resultado analitico
    ya = lambda n: (-1.26*(0.25**n)+0.444*((-0.2)**n)+5.81*(0.8**n))*u(n)
    yia = ya(ni)

    errado = yia-yi
    erradomax = np.max(np.abs(errado))

        
# SALIDA
print('respuesta entrada cero: ')
print('raices: ', gamma)
if escomplejo == 0:
    print('Matriz: ')
    print(A)
    print('Ci:     ', ci)
    print('yn:  ')
    sym.pprint(y0)
    
    print('\n respuesta impulso: ')
    print('hi:',hi)
    print('Matriz: ')
    print(Ah)
    print('Ch: ',ch)
    print('n>=0, hn:  ')
    sym.pprint(hn)

    print('\n error numerico vs analitico: ')
    print(' maximo de abs(error): ', erradomax)
    print(' errado: ')
    print(errado)

    # SALIDA - GRAFICA
    plt.figure(1)
    plt.suptitle('Suma de Convolucion x[n]*h[n]')

    plt.subplot(311)
    plt.stem(ni,xi, linefmt='b--',
             markerfmt='bo',basefmt='k-')
    plt.ylabel('x[n]')

    plt.subplot(312)
    plt.stem(ni,hi, linefmt='b--',
             markerfmt='ro',basefmt='k-')
    plt.ylabel('h[n]')

    plt.subplot(313)
    plt.stem(ni,yi, linefmt='g-.',
             markerfmt='mo', basefmt='k-')
    plt.stem(ni,yia, linefmt='y-.',
             markerfmt='mD', basefmt='k-')

    plt.ylabel('x[n]*h[n]')
    plt.xlabel('n')

    plt.show()
    
else:
    print(' existen raices con números complejos.')
    print(' usar algoritmo de la sección correspondiente.')

6.3.1 LTI DT – Respuesta impulso h[n]. Ejercicio con Python

Referencia: Lathi Ejemplo 3.17, 3.18  y 3.19 p277-278

Determine la respuesta al impulso unitario de un sistema LTID descrito por la ecuación de diferencias mostrada,

y[n+2] - 0.6 y[n+1] - 0.16 y[n] = 5x[n+2]

Para el diagrama se usa la ecuación desplazada en dos unidades,

y[n] - 0.6 y[n-1] - 0.16 y[n-2] = 5x[n] y[n] = 0.6 y[n-1] + 0.16 y[n-2] + 5x[n]

En la entrada se usa un impulso unitario x[n] = δ[n]

Desarrollo Analítico

Se requieren al menos dos valores iniciales en el ejercicio, por lo que se emplea la ecuación en forma iterativa, recordando que x[n] = δ[n], siendo entonces y[n] = h[n]

y[n] - 0.6 y[n-1] - 0.16 y[n-2] = 5x[n] h[n] - 0.6 h[n-1] - 0.16 h[n-2] = 5\delta [n]

aplicando para n=0

h[0] - 0.6 h[-1] - 0.16 h[-2] = 5\delta[0] h[0] - 0.6 (0) - 0.16 (0) = 5(1) h[0] = 5

luego se aplica para n = 1

h[1] - 0.6 h[0] - 0.16 h[-1] = 5 \delta[1] h[1] - 0.6 (5) - 0.16 (0) = 5(0) h[1] = 3

El ejercicio es continuación del primer ejercicio de respuesta a entrada cero, por lo que simplificamos el desarrollo como

(E^2 - 0.6 E - 0.16) y[n] = 5 E^2 x[n] \gamma^2 - 0.6 \gamma - 0.16 = 0

con raíces características γ1=-0.2 y γ2=0.8,

con los modos característicos se tiene que

y_c[n] = c_1 (-0.2)^n + c_2 (0.8)^n

que se convierte a:

h[n] = [c_1 (-0.2)^n + c_2 (0.8)^n ] \mu [n]

Para determinar c1 y c2 se recurre a encontrar dos valores de forma iterativa, con n=0 y n=1.

h[0] = [c_1 (-0.2)^0 + c_2 (0.8)^0 ] \mu [0] h[0] = c_1 + c_2 h[1] = [c_1 (-0.2)^1 + c_2 (0.8)^1 ] \mu [1] h[1] = -0.2 c_1 + 0.8 c_2

los valores de h[0]=5 y h[1]=3 se encontraron de forma iterativa

5 = c_1 + c_2 3 = -0.2 c_1 + 0.8 c_2

resolviendo con Python:

>>> import numpy as np
>>> A = [[ 1., 1.],
         [-0.2, 0.8]]
>>> B = [5.,3.]
>>> np.linalg.solve(A,B)
array([1., 4.])
>>> 

encontrando que c1=1 y c2=4

teniendo como resultado final:

h[n] = [ (-0.2)^n +4 (0.8)^n ] \mu [n]


Algoritmo en Python

La respuesta para el procedimiento anterior realizada con Python es:

 respuesta impulso: 
hi: [5. 3.]
Matriz: 
[[ 1.   1. ]
 [ 0.8 -0.2]]
Ch:  [4. 1.]
n>=0, hn:  
        n          n
1.0*-0.2  + 4.0*0.8 
>>>

se desarrolla el algoritmo a partir de entrada cero, continuando con el cálculo iterativo de h[n] para los valores iniciales, luego determina los valores de los coeficientes de la ecuación h[n]

# Sistema LTID. Respuesta impulso
# QE con raices Reales NO repetidas Lathi ejemplo 3.13 pdf271
# Lathi ejemplo 3.18 y 3.19 pdf278,
# blog.espol.edu.ec/telg1001
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt

# INGRESO
# coeficientes E con grado descendente
QE = [1., -0.6, -0.16]
PE = [5.,  0,    0   ]
# condiciones iniciales ascendente ...,y[-2],y[-1]
inicial = [25/4, 0.]

tolera = 1e-6  # casi_cero
muestras = 10  # para grafica

# PROCEDIMIENTO
# Respuesta a ENTRADA CERO
# raices, revisa numeros complejos
gamma = np.roots(QE)
revisaImag = np.iscomplex(gamma)
escomplejo = np.sum(revisaImag)

# coeficientes de ecuacion
m_q = len(QE)-1
Ac = np.zeros(shape=(m_q,m_q),dtype=float)

# revisa si parte compleja <tolera o casi_cero 
if escomplejo>0:
    for i in range(0,m_q,1):
        valorimag = np.imag(gamma[i])
        if np.abs(valorimag)<tolera:
            gamma[i] = float(np.real(gamma[i]))
    sumaj = np.sum(np.abs(np.imag(gamma)))
    if sumaj <tolera:
        print(sumaj)
        gamma = np.real(gamma)
        escomplejo = 0

# revisa repetidos
unicoscuenta = np.unique(gamma,return_counts=True)
repetidas = np.sum(unicoscuenta[1]-1)

# Respuesta impulso h[n]
ki = np.arange(-m_q,m_q,1)
hi = np.zeros(m_q, dtype=float)
xi = np.zeros(2*m_q, dtype=float)
xi[m_q] = 1 # impulso en n=0
Ah = np.zeros(shape=(m_q,m_q),dtype=float)

# h[n] iterativo
p_n = len(PE)
for i in range(0,m_q,1):
    for k in range(m_q,2*m_q,1):
        hi[i] = hi[i] - QE[k-m_q]*hi[m_q-k+1]
    for k in range(0,p_n,1):
        hi[i] = hi[i] + PE[k]*xi[m_q+i]
Bh = np.copy(hi[:m_q])

# coeficientes de ecuacion
for i in range(0,m_q,1):
    for j in range(0,m_q,1):
        Ah[i,j] = gamma[j]**(i)
ch = np.linalg.solve(Ah,Bh)

# ecuacion hn
n = sym.Symbol('n')
hn = 0*n
for i in range(0,m_q,1):
    hn = hn + ch[i]*(gamma[i]**n)

# SALIDA
if escomplejo == 0: 
    print('\n respuesta impulso: ')
    print('Bh:',Bh)
    print('Matriz: ')
    print(Ah)
    print('Ch: ',ch)
    print('n>=0, hn:  ')
    sym.pprint(hn)
else:
    print(' existen raices con números complejos.')
    print(' usar algoritmo de la sección correspondiente.')
    

# grafica datos
ki = np.arange(-m_q,muestras,1)
hi = np.zeros(muestras+m_q)

if escomplejo == 0: 
    # evaluación de h[n]
    h_n = sym.lambdify(n,hn)
    hi[m_q:] = h_n(ki[m_q:])

    # grafica h[n]
    plt.stem(ki,hi,label='h[n]',
             markerfmt ='C1o',
             linefmt='C2--')
    plt.legend()
    plt.grid()
    plt.ylabel('h[n]')
    plt.xlabel('ki')
    plt.title('h[n] = '+str(hn))
    plt.show()

Comentario: Aunque es relativamente simple determinar la respuesta al impulso h[n] usando el método descrito, el desarrollo del tema se realiza mejor en la unidad con Transformada z. Lathi p280.

6.2.2 LTI DT – Respuesta entrada cero – Resumen algoritmo Python

El algoritmo integra los casos presentados en los ejercicios anteriores para:

1. raíces reales diferentes
2. raíces con números complejos
3. raíces reales repetidas


Instrucciones en Python

EL detalle de los pasos se encuentra en los ejercicios con Python que se han integrado por caso.

# Sistema LTID. Respuesta entrada cero
# QE con raices Reales NO repetidas Lathi ejemplo 3.13 pdf271
# QE con raices Reales Repetidas Lathi ejemplo 3.15 p274
# QE con raices Complejas Lathi ejemplo 3.16 p275
# blog.espol.edu.ec/telg1001
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt

# INGRESO
# coeficientes E con grado descendente
QE = [1.0, -1.56, 0.81]
PE = [0., 1., 3.]
# condiciones iniciales ascendente ...,y[-2],y[-1]
inicial = [1.,2.]

tolera = 1e-6  # casi_cero

# PROCEDIMIENTO
# Respuesta a ENTRADA CERO
# raices, revisa numeros complejos
gamma = np.roots(QE)
revisaImag = np.iscomplex(gamma)
escomplejo = np.sum(revisaImag)

# coeficientes de ecuacion
m_q = len(QE)-1
Ac = np.zeros(shape=(m_q,m_q),dtype=float)

# revisa si parte compleja <tolera o casi_cero 
if escomplejo>0:
    for i in range(0,m_q,1):
        valorimag = np.imag(gamma[i])
        if np.abs(valorimag)<tolera:
            gamma[i] = float(np.real(gamma[i]))
    sumaj = np.sum(np.abs(np.imag(gamma)))
    if sumaj <tolera:
        print(sumaj)
        gamma = np.real(gamma)
        escomplejo = 0

# revisa repetidos
unicoscuenta = np.unique(gamma,return_counts=True)
repetidas = np.sum(unicoscuenta[1]-1)

# Determina coeficientes ci de Y[n]
# raices Reales no repetidas
if escomplejo == 0 and repetidas==0:
    for i in range(0,m_q,1):
        for j in range(0,m_q,1):
            Ac[i,j] = gamma[j]**(-m_q+i)
    ci = np.linalg.solve(Ac,inicial)
    
# raices Reales repetidas
if escomplejo == 0 and repetidas > 0:
    for i in range(0,m_q,1):
        for j in range(0,m_q,1):
            Ac[i,j] = ((-m_q+i)**j)*gamma[j]**(-m_q+i)
    ci = np.linalg.solve(Ac,inicial)

# raices Complejas
if escomplejo > 0:
    g_magnitud = np.absolute(gamma)
    g_angulo = np.angle(gamma)

    for i in range(0,m_q,1):
        k = -(m_q-i)
        a = np.cos(np.abs(g_angulo[i])*(k))*(g_magnitud[i]**(k))
        b = -np.sin(np.abs(g_angulo[i])*(k))*(g_magnitud[i]**(k))
        Ac[i] = [a,b]
    Ac = np.array(Ac)
    cj = np.linalg.solve(Ac,inicial)

    theta = np.arctan(cj[1]/cj[0])
    ci = cj[0]/np.cos(theta)

# ecuacion y0 entrada cero
n = sym.Symbol('n')
y0 = 0*n

if escomplejo == 0 and repetidas==0:
    for i in range(0,m_q,1):
        y0 = y0 + ci[i]*(gamma[i]**n)
        
if escomplejo == 0 and repetidas > 0:
    for i in range(0,m_q,1):
        y0 = y0 + ci[i]*(n**i)*(gamma[i]**n)
    y0 = y0.simplify()

if escomplejo > 0:
    y0 = ci*(g_magnitud[0]**n)*sym.cos(np.abs(g_angulo[i])*n - theta)
    
# SALIDA
print('respuesta entrada cero: ')
print('raices: ', gamma)
if escomplejo == 0:
    if repetidas>0:
        print('Raices repetidas: ', repetidas)
    print('Matriz: ')
    print(Ac)
    print('Ci:     ', ci)
    print('y0:')
    sym.pprint(y0)
if escomplejo > 0:
    print('raices complejas: ', escomplejo)
    print('magnitud:',g_magnitud)
    print('theta:',g_angulo)
    print('Matriz: ')
    print(Ac)
    print('Cj: ', cj)
    print('Ci: ',ci)
    print('y0: ')
    sym.pprint(y0)