3.6 Busca impulso unitario δ(t) o escalón unitario μ(t) con Sympy-Python

En los sistemas lineales, la causalidad se muestra con respuestas para t≥0, por lo que se usan términos multiplicados por escalón unitario μ(t). Mientras que la respuesta al impulso genera al menos un término con impulso unitario δ(t) con un coeficiente constante.

Para el desarrollo de los ejercicios, es de gran utilidad disponer de una función que permita buscar el impulso unitario dentro de una expresión de varios términos. Listar las ubicaciones de los impulsos es un resultado que se usa en la generación de gráficas, pues no siempre la muestra en el intervalo de tiempo usado se encuentra exactamente sobre la aplicación del impulso unitario.

Se propone usar dos funciones. una para encontrar dónde ocurre el impulso y otra para buscar donde y el sentido del escalón unitario.


1. Busca impulso unitario, DiracDelta(t) o δ(t) con Sympy

Si f(t) fuese solo δ(t), δ(t-1) o δ(t+1), el valor de desplazamiento de obtiene con el primer argumento de la función mediante ft.args[0] que al evaluarlo en cero y multiplicarlo por (-1) se tendría donde se aplica.

ecuacion = sym.Eq(ft.args[0],0)
donde = sym.solve(ecuacion,t)

Para los casos como δ(-t-1), δ(-t+1) es necesario trabajar con el signo de la variable t. Una forma de evaluar la expresión interiores de los impulsos es obtener la ecuacion de la forma at+b=0 con sym.Eq(ft.args[0],0) y luego despejar el valor de t con la instrucción sym.solve(ecuacion,t).

En expresiones simples de un solo término con coeficientes, se tiene:

h = 2*sym.pi*d.subs(t,t-2)

con resultados como:

h(t):
2*pi*DiracDelta(t - 2)
busca_impulso(h):  [2]

busca impulso sympy 01

En el caso que se usen términos de varios impulsos, el valor de interés se encuentra usando el mínimo de la lista donde y observando que aún sea ≥0.

h = d.subs(t,t-1) + 2*sym.pi*d.subs(t,t-2)

con resuldado esperado de algoritmo

h(t):
2*pi*DiracDelta(t - 2) + DiracDelta(t - 1)
busca_impulso(h):  [1, 2]

busca impulso Sympy 02

Instrucciones en Python

# Busca impulso unitario en h(t)
# http://blog.espol.edu.ec/telg1001/busca-impulso-unitario-o-escalon-unitario-con-sympy/
import sympy as sym
import matplotlib.pyplot as plt
import telg1001 as fcnm

# INGRESO
t = sym.Symbol('t',real=True)
u = sym.Heaviside(t)
d = sym.DiracDelta(t)
a = sym.Symbol('a',real=True)

# busca impulso en h(t)
# h = d
# h = 2*sym.pi*d.subs(t,t-2)
h = d.subs(t,t-1) + 2*sym.pi*d.subs(t,t-2)
# h = 3*d.subs(t,t+1)
# h = 5*sym.exp(-t)*d
# h = 4

# grafica intervalo [t_a,t_b] simetrico a 0
t_b = 4 ; t_a = -t_b
muestras = 51

# PROCEDIMIENTO
def busca_impulso(ft):
    ''' busca en f(t) impulsos sym.DiracDelta
        entrega una lista ordenada de resultados
    '''
    def impulso_donde(ft):
        ''' busca posicion de impulso en ft simple d, d**2
            un solo termino,sin factores
        '''
        ft = sym.sympify(ft,t) # convierte a sympy si es constante
        donde = [] # revisar f(t) sin impulsos
        if ft.has(sym.DiracDelta):
            if not(ft.is_Pow): # sin exponentes
                ecuacion = sym.Eq(ft.args[0],0)
            else: # con exponentes d**2
                ecuacion = sym.Eq(ft.args[0].args[0],0)
            donde = sym.solve(ecuacion,t)
        return(donde)
    
    def impulso_donde_unterm(ft):
        ''' revisa impulso en un termino suma de ft
        '''
        donde = [] # revisar f(t) sin impulsos
        factor_mul = sym.Mul.make_args(ft)
        for factor_k in factor_mul:
            if factor_k.has(sym.DiracDelta):
                donde_k = impulso_donde(factor_k)
                if len(donde) == 0: # vacio
                    donde.extend(donde_k)
                if len(donde)>0: # sin repetir
                    if not(donde_k[0] in donde): 
                        donde = [] # anulado por d(t-a)*d(t-b)
        return(donde)

    # revisa terminos suma
    ft = sym.sympify(ft,t) # convierte a sympy si es constante
    ft = sym.expand(ft)
    respuesta = []
    term_suma = sym.Add.make_args(ft)
    for term_k in term_suma:
        donde = impulso_donde_unterm(term_k)
        if not(donde in respuesta) and len(donde)>0:
            respuesta.extend(donde)
    if len(respuesta)>1: # ordena ascendente
        respuesta.sort()
    respuesta = list(respuesta)
    return(respuesta)

donde_d = busca_impulso(h)

# SALIDA
print('h(t):')
sym.pprint(h)
print('busca_impulso(h): ',donde_d)

# Grafica
figura_h = fcnm.graficar_ft(h,t_a,t_b,muestras,f_nombre='h')
plt.show()

1. Busca escalón unitario, Heaviside(t) o μ(t) con Sympy

Para pruebas se tienen algunas expresiones de h(t), recordando que se analiza expresiones de un solo término, sin sumas o mutiplicaciones.

# entrada x(t)
# h = u
h = u.subs(t,t-1)
# h = u.subs(t,t+1)
# h = 5

Semejante al ejemplo anterior, se busca dónde se produce la transición del escalón unitario y el sentido si es de subida o de bajada como el signo de ‘t’.

\frac{d}{dt}\mu (t) = \delta(t)

Con la función busca_impulso() obtiene donde se produce el impulso.

El sentido del escalón se obtiene al derivar la expresión del argumento. Si el escalón se desarrolla hacia de donde se produce, será positivo, y hacia la izquierda será negativo. Al aplicar escalon_donde(ft) permite determinar el punto de subida y bajada.

Para determinar el sentido del escalón unitario, se procede a tomar la expresion del argumento ft.args[0] y luego derivar con la  instrucción sym.diff(ft.args[0]) para obtener el signo_t. con esto se observa si el desarrollo es hacia el lado derecho del plano (RHS). Con los parametros de donde y el sentido del escalón se puede aplicar un criterio de causalidad.

    def escalon_donde(ft):
        ''' ft sin factores o terminos suma
        '''
        ft = sym.sympify(ft,t) # convierte a sympy si es constante
        respuesta = []
        if ft.has(sym.Heaviside):
            eq_u      = sym.Eq(ft.args[0],0)
            sentido   = sym.diff(eq_u.lhs,t,1)
            donde     = sym.solve(eq_u,t)[0]
            respuesta = [donde,sentido]
        return(respuesta)

Instrucciones en Python

# Busca escalon unitario en h(t)
# http://blog.espol.edu.ec/telg1001/busca-impulso-unitario-o-escalon-unitario-con-sympy/
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
equivalentes = [{'DiracDelta': lambda x: 1*(x==0)},
                {'Heaviside': lambda x,y: np.heaviside(x, 1)},
                'numpy',]
import telg1001 as fcnm

# INGRESO
t = sym.Symbol('t',real=True)
k = sym.Symbol('k',real=True)
u = sym.Heaviside(t)
d = sym.DiracDelta(t)

# entrada h(t)
# h = u
h = u.subs(t,t-1)
# h = u.subs(t,t+1)
# h = 5
# h = u + d
# h = 3*sym.pi*u.subs(t,t-1)+ 2*d
# h = sym.exp(-2*t)*u.subs(t,t+1) + sym.exp(-4*t)*u.subs(t,t-3)
# h = sym.exp(-2*t)*sym.cos(t) + 4

# h = u.subs(t,-t+2)*u.subs(t,-t+1)
# h = u.subs(t,-t+1)*u.subs(t,-t+1)
# h = 3*u.subs(t,-t+1)*u.subs(t,-t+1)
# h = u.subs(t,t-1)-u.subs(t,t-2)

# grafica intervalo [t_a,t_b] simetrico a 0
t_b = 4 ; t_a = -t_b
muestras = 51

# PROCEDIMIENTO
def busca_escalon(ft):
    ''' busca en f(t) el donde,sentido de escalon unitario
        en un termino simple  sin sumas, para ubicar
        lado del plano de f(t)
    '''
    
    def escalon_donde(ft):
        ''' ft sin factores o terminos suma
        '''
        ft = sym.sympify(ft,t) # convierte a sympy si es constante
        respuesta = []
        if ft.has(sym.Heaviside):
            eq_u      = sym.Eq(ft.args[0],0)
            sentido   = sym.diff(eq_u.lhs,t,1)
            donde     = sym.solve(eq_u,t)[0]
            respuesta = [donde,sentido]
        return(respuesta)

    def escalon_donde_unterm(ft):
        '''revisa termino de factores multiplica
        '''
        respuesta = []
        factor_mul = sym.Mul.make_args(ft)
        for factor_k in factor_mul:
            if factor_k.has(sym.Heaviside):
                ubicado = []
                if not(factor_k.is_Pow): # sin exponente
                    ubicado = escalon_donde(factor_k)
                else:  # con exponentes d**k
                    ubicado = escalon_donde(factor_k.args[0])
                if len(ubicado)>0:
                    respuesta.append(ubicado)
        return(respuesta)
        
    # revisa terminos suma
    ft = sym.sympify(ft,t) # convierte a sympy si es constante
    ft = sym.expand(ft)
    respuesta = []
    term_suma = sym.Add.make_args(ft)
    for term_k in term_suma:
        donde = escalon_donde_unterm(term_k)
        if not(donde in respuesta) and len(donde)>0:
            respuesta.extend(donde)
    if len(respuesta)>1: # ordena ascendente
        respuesta = np.array(respuesta)
        columna   = respuesta[:,0]
        ordena    = np.argsort(columna)
        respuesta = respuesta[ordena]

    return(respuesta)

donde_u = busca_escalon(h)

# SALIDA
print('h(t):')
sym.pprint(h)
print('busca_impulso:',fcnm.busca_impulso(h))
if len(donde_u)<=1:
    print('busca_escalon:',donde_u)
else:
    print('busca_escalon:')
    print(busca_escalon(h))

# Grafica
figura_h = fcnm.graficar_ft(h,t_a,t_b,f_nombre='h')
plt.show()

Las funciones se las incorpora a los algoritmos telg1001.py

..