2.6 D-to-C - Interpolación con pulso triangular

[ interpola triángulo ] [ ejemplo ] [ algoritmo ] [ gráfica interactiva ]
..


1. Interpolación Lineal

Referencia: McClellan 4-3.3 p143

La interpolación lineal, con pulso triangular, o polinomio de primer orden consiste en los segmentos de línea:

p(t) = \begin{cases} 1-\frac{|t|}{Ts} & -T_s \leq t \leq -\frac{1}{2} T_s \\ 0 & otro caso \end{cases}

pulso Triangular 01El ancho del pulso es de dos veces el periodo de muestreo Ts, lo que causa que los pulsos se superpongan entre muestras. El resultado de la interpolación de varios impulsos requiere que se sumen las partes superpuestas, situación a considerar en la variable tiempo t del resultado y que se resume en la función pulso_ti().

1.1 Algoritmo en Python para un pulso triangular

# D-to-C, Interpolacion con pulso triangular
# ejemplo 4.3.3 p143
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
fs = 200  # Hz muestreo
fs_veces = 10 # suavizar x(t), sobremuestreo
titulo = 'pulso triangular'
nT = 2.0  # graficar periodos de la señal

# pulso triangular
pulso_causal = True # No causal, pulso centrado en cero

# PROCEDIMIENTO
Ts = 1/fs # muestreo periodo
#https://blog.espol.edu.ec/telg1001/senales-escalon-%ce%bct-e-impulso-%ce%b4t/
#u = lambda t: np.piecewise(t,t>=0,[1,0])
u = lambda t: np.heaviside(t,1)
# pulso triangular
u1 = lambda t: u(t+Ts)
u2 = lambda t: u(t-Ts)
triangular = lambda t: (1-abs(t)/Ts)*(u1(t) - u2(t))             
pulso_ancho = 2

# x[n] interpolacion en Ts, a muestreo fs
Ts = 1/fs ; dtc = Ts/fs_veces
t_unpulso = np.arange(-Ts*nT,Ts*nT+dtc,dtc)
unpulso = triangular(t_unpulso)

# SALIDA
print('muestras:',len(t_unpulso))
print('t_unpulso:',t_unpulso)

# GRAFICA de un pulso
# p(t) componentes
plt.plot(t_unpulso,unpulso, color='green',linestyle='dashed',
         label='p_tri(t)')
plt.axvline(-Ts,color='red',linestyle='dotted')
plt.axvline(Ts,color='red',linestyle='dotted')
plt.stem([0],[1],linefmt = 'C1:', label='xn[0]')
plt.axhline(0,color='Black')

# grafica entorno
plt.xlabel('t')
plt.ylabel('amplitud')
texto = titulo + ' ; fs='+str(fs)
texto = texto +' ; Ts='+str(1/fs)
plt.title(texto)
plt.grid()
plt.legend()

plt.tight_layout()
plt.show()

[ interpola triángulo ] [ ejemplo ] [ algoritmo ] [ gráfica interactiva ]
..


2. Ejemplo con señal Senoidal y pulsos triangular

La señal x(t) tipo cosenoidal con f0=83 Hz y de amplitud unitaria,  se convierte a muestras discretas con frecuencia fs=200 Hz.

x(t) = \cos(2\pi (83)t)

interpola Pulso Triangular animate

Cada pulso triangular aplicado en una muestra se superpone al siguiente aplicado en la muestra nTs.

interpola Pulso Triangular 01En la reconstrucción para el vector tiempo t_pulsos, se debe considerar que el primer pulso empieza antes que el valor de la muestra, por lo que la señal presenta un retraso de mitad de pulso.

interpola Pulso Triangular 02En este caso, como el ancho del pulso es 2Ts, la señal resultante se atrasa Ts com se muestra en la gráfica.

La función pulso_ti() construye el vector de tiempo considerando ancho del pulso y el número de muestras x[n]. El inicio del vector tiempo también considera si el sistema es causal al aplicar el atraso a la unidad de tiempo, pues la reconstrucción empieza en el tiempo cero para las señales en tiempo real.

El vector tiempo también debe considerar la frecuencia de muestreo fs y para la gráfica el sobre-muestreo fs_veces. Al final se observa que la señal reconstruida tiene una duración adicional al tiempo de la última muestra, por lo que se añade una duración de mitad de pulso al extremo final. El efecto se observa en la señal x(t)_pulsos.

[ interpola triángulo ] [ ejemplo ] [ algoritmo ] [ gráfica interactiva ]
..


3.Algoritmo en Python Interpola con pulso triangular

En la reconstrucción de la señal x(t) usando pulsos, se realiza un proceso semejante al aplicado en pulso_ti() , en las partes que se superponen los impulsos se suman para dar un resultado de interpolación lineal de rectas que unen los valores de las muestras.

# D-to-C, Interpolacion con pulso triangular
# ejemplo 4.3.3 p143
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
# señal x(t)
f0 = 83   # frecuencia de senal
fase0 = 0 # [0,2*np.pi]
xt = lambda t: np.cos(2*np.pi*f0*t + fase0)

fs = 200  # Hz muestreo
fs_veces = 20 # suavizar x(t), sobremuestreo
nT = 2.4  # graficar periodos de la señal

titulo = 'pulso triangular'
# pulso triangular
pulso_causal = True # No causal, pulso centrado en cero
pulso_ancho = 2

casicero = 1e-10 # cero para valores menores

# PROCEDIMIENTO
Ts = 1/fs # tamaño de paso con fs
T =  1/f0 # periodo de señal
dtc = Ts/fs_veces # suavizar x(t), sobremuestreo

# muestreo x(t)
ti = np.arange(0,nT*T+dtc,dtc)
xti = xt(ti)
ti_max = max(ti)

# muestreo x[n]
muestras_n = 2
if ti_max>=Ts: # varias muestras
    muestras_n = int(ti_max/Ts)+1
ki  = np.arange(0,muestras_n,1,dtype=int)
tki = ki*Ts # muestras x[n]
xki = xt(tki)
tkj = tki   # x[n] alias0
ti_0 = ti   # No Causal, pulso centrado en cero
if pulso_causal: # Causal
    tkj = tki + Ts*pulso_ancho/2 
    ti_0 = ti + Ts*pulso_ancho/2
xti_0 = xti

def pulso_ti(muestras_n,fs,fs_veces,ancho=1,causal=True):
    ''' tiempos para las n muestras x(t) con pulsos a frecuencia fs.
    Para suavizar el pulso se usa fs_veces para el sobremuestreo.
    El ancho del pulso es veces el periodo de muestreo (1/Ts)
    Si es causal el tiempo inicia en t=0, centrado en cero.
    Si no es causal el tiempo inicia t en mitad de intervalo.
    '''
    Ts = 1/fs # muestreo periodo
    dtc = Ts/fs_veces # suavizar pulso(t), sobremuestreo
    t_Ts = np.arange(0,Ts,dtc) # tiempo en periodo Ts sobremuestreado
    
    mitad = Ts*ancho/2
    t_mitad = np.arange(0,mitad,dtc)
    t_pulsos = np.copy(t_mitad) # mitad de primer pulso
    for i in range(1,muestras_n,1):
        t_pulsos = np.concatenate((t_pulsos,t_Ts + t_pulsos[-1]+dtc))
    # mitad de último pulso para muestra_n
    t_pulsos = np.concatenate((t_pulsos,t_mitad + t_pulsos[-1]+dtc))

    # https://blog.espol.edu.ec/telg1001/sistemas-causales-y-no-causales/
    if causal == False: # centrado en cero
        t_pulsos = t_pulsos - mitad
    return(t_pulsos)

# x[n] interpolacion en Ts, a muestreo fs
t_pulsos = pulso_ti(muestras_n,fs,fs_veces,
                    pulso_ancho,pulso_causal)

#https://blog.espol.edu.ec/telg1001/senales-escalon-%ce%bct-e-impulso-%ce%b4t/
#u = lambda t: np.piecewise(t,t>=0,[1,0])
u = lambda t: np.heaviside(t,1)
# pulso triangular
u1 = lambda t: u(t+Ts*pulso_ancho/2)
u2 = lambda t: u(t-Ts*pulso_ancho/2)
triangular = lambda t: (1-abs(t)*fs)*(u1(t) - u2(t))             

# unpulso muestreo
t_unpulso = pulso_ti(1,fs,fs_veces,
                     pulso_ancho,pulso_causal)
t_pulsoEval= t_unpulso
if pulso_causal:
    t_pulsoEval = t_unpulso-Ts*pulso_ancho/2
unpulso = triangular(t_pulsoEval)
muestras_pulso = len(t_unpulso)

muestras_N = len(xki)
mitad_pulso = int(muestras_pulso/2)
pulsos_vacio = np.zeros(len(t_pulsos),dtype=float)
xn_pulsos = np.copy(pulsos_vacio)
xk_pulsos = []
for j in range(0,muestras_N,1): # x(t) pulsos
    k0 = int(j*fs_veces)
    kn = k0 + muestras_pulso
    pulsoj = np.copy(pulsos_vacio)
    pulsoj[k0:kn]= pulsoj[k0:kn] + unpulso*xki[j]
    xk_pulsos.append(pulsoj)
    xn_pulsos = xn_pulsos+pulsoj

    
# SALIDA
print('muestras_pulso:',muestras_pulso)
print('t_unpulso:',t_unpulso)
print('muestras_tiempo:',len(t_pulsos))

# GRAFICAS ----------------------------
fig, [graf_t,graf_n] = plt.subplots(2,1)

# x(t) grafico entorno
t_causal = 0
if pulso_causal:
    t_causal = Ts*pulso_ancho/2
graf_t.axhline(0,color='black')
graf_t.axvline(0,color='black')
graf_t.set_xlabel('t')
graf_t.set_ylabel('amplitud')
graf_t.set_xlim([t_pulsos[0]-t_causal,
                 t_pulsos[-1]-t_causal])
graf_t.grid()
texto = titulo + ' ; f0='+str(f0)
texto = texto + ' ; fs='+str(fs)
texto = texto + ' ; causal:'+str(pulso_causal)
graf_t.set_title(texto)
# x(t) componentes
graf_t.plot(ti,xti,label='x(t)')
graf_t.stem(tki,xki,label='x[n]',linefmt = 'C1:')
for i in range(0,muestras_N,1):
    graf_t.plot(t_pulsos-t_causal,xk_pulsos[i],
                linestyle='dashed')
graf_t.legend()

# x[n] grafico entorno
graf_n.axhline(0,color='black')
graf_n.axvline(0,color='black')
graf_n.set_xlabel('t')
graf_n.set_ylabel('amplitud')
graf_n.set_xlim([t_pulsos[0],t_pulsos[-1]])
graf_n.grid()
# x[n] componentes
graf_n.plot(ti_0,xti_0,linestyle='dotted',
            label='x(t)')
graf_n.stem(tkj,xki,label='x[n]',linefmt = 'C1:')
graf_n.plot(t_pulsos,xn_pulsos,
            label='x(t) pulso',
            lw=2, color='orange')

graf_n.legend()
plt.tight_layout()
plt.show()

[ interpola triángulo ] [ ejemplo ] [ algoritmo ] [ gráfica interactiva ]
..


4. Algoritmo en Python para gráfico interactivo con fs

interpola Pulso Triangular animateLas instrucciones en Python son semejantes al caso con pulso rectangular.
Para dibujar cada pulso, se usa un objeto de líneas agrupadas o LineCollection() que permite actualizar las lineas al actualizar la tabla.

# D-to-C, Interpolacion con pulso triangular
# grafico interactivo
# ejemplo 4.3.3 p143
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
# señal x(t)
f0 = 83   # frecuencia de senal
fase0 = 0 # [0,2*np.pi]
xt = lambda t: np.cos(2*np.pi*f0*t + fase0)

fs = 200  # Hz muestreo
fs_veces = 20 # suavizar x(t), sobremuestreo
nT = 2.4  # graficar periodos de la señal

titulo = 'pulso triangular'
# pulso triangular
pulso_causal = True # No causal, pulso centrado en cero
pulso_ancho = 2

casicero = 1e-10 # cero para valores menores

# PROCEDIMIENTO
Ts = 1/fs # tamaño de paso con fs
T =  1/f0 # periodo de señal
dtc = Ts/fs_veces # suavizar x(t), sobremuestreo

def xt_actualiza(xt,f0,fs,fs_veces,nT,
                 pulso_ancho=1,pulso_causal=True):
    ''' x(t) muestreada a fs, para nT periodos de x(t).
    gráfica suavizada con fs_veces. No causal, pulso centrado en cero.
    Causal el pulso inicia en t=0.
    '''
    Ts = 1/fs # tamaño de paso con fs
    T =  1/f0 # periodo de señal
    dtc = Ts/fs_veces # suavizar x(t), sobremuestreo
    
    # muestreo x(t)
    ti = np.arange(0,nT*T+dtc,dtc)
    xti = xt(ti)
    ti_max = max(ti)

    # muestreo x[n]
    muestras_n = 2
    if ti_max>=Ts: # varias muestras
        muestras_n = int(ti_max/Ts)+1
    ki  = np.arange(0,muestras_n,1,dtype=int)
    tki = ki*Ts # muestras x[n]
    xki = xt(tki)
    tkj = tki   # x[n] alias0
    ti_0 = ti   # No Causal, pulso centrado en cero
    if pulso_causal: # Causal
        tkj = tki + Ts*pulso_ancho/2 
        ti_0 = ti + Ts*pulso_ancho/2
    xti_0 = xti
    return(ti,xti,tki,xki,tkj,ti_0,xti_0)

xt_list = xt_actualiza(xt,f0,fs,fs_veces,nT,
                       pulso_ancho,pulso_causal)
[ti,xti,tki,xki,tkj,ti_0,xti_0] = xt_list # x(t),x[n],x(t)_alias0
muestras_n = len(tki)

def pulso_ti(muestras_n,fs,fs_veces,ancho=1,causal=True):
    ''' tiempos para las n muestras x(t) con pulsos a frecuencia fs.
    Para suavizar el pulso se usa fs_veces para el sobremuestreo.
    El ancho del pulso es veces el periodo de muestreo (1/Ts)
    Si es causal el tiempo inicia en t=0, centrado en cero.
    Si no es causal el tiempo inicia t en mitad de intervalo.
    '''
    Ts = 1/fs # muestreo periodo
    dtc = Ts/fs_veces # suavizar pulso(t), sobremuestreo
    t_Ts = np.arange(0,Ts,dtc) # tiempo en periodo Ts sobremuestreado
    
    mitad = Ts*ancho/2
    t_mitad = np.arange(0,mitad,dtc)
    t_pulsos = np.copy(t_mitad) # mitad de primer pulso
    for i in range(1,muestras_n,1):
        t_pulsos = np.concatenate((t_pulsos,t_Ts + t_pulsos[-1]+dtc))
    # mitad de último pulso para muestra_n
    t_pulsos = np.concatenate((t_pulsos,t_mitad + t_pulsos[-1]+dtc))

    # https://blog.espol.edu.ec/telg1001/sistemas-causales-y-no-causales/
    if causal == False: # centrado en cero
        t_pulsos = t_pulsos - mitad
    return(t_pulsos)

def xn_actualiza(fs,fs_veces,xki,
                 pulso_ancho=1,pulso_causal=True):
    Ts = 1/fs # tamaño de paso con fs
    T =  1/f0 # periodo de señal
    dtc = Ts/fs_veces # suavizar x(t), sobremuestreo
    
    #https://blog.espol.edu.ec/telg1001/senales-escalon-%ce%bct-e-impulso-%ce%b4t/
    #u = lambda t: np.piecewise(t,t>=0,[1,0])
    u = lambda t: np.heaviside(t,1)
    # pulso triangular
    u1 = lambda t: u(t+Ts*pulso_ancho/2)
    u2 = lambda t: u(t-Ts*pulso_ancho/2)
    triangular = lambda t: (1-abs(t)*fs)*(u1(t) - u2(t))             

    # x[n] interpolacion en Ts, a muestreo fs
    muestras_n = len(xki)
    t_pulsos = pulso_ti(muestras_n,fs,fs_veces,
                    pulso_ancho,pulso_causal)
    
    # unpulso muestreo
    t_unpulso = pulso_ti(1,fs,fs_veces,
                         pulso_ancho,pulso_causal)
    t_pulsoEval= t_unpulso
    if pulso_causal:
        t_pulsoEval = t_unpulso-Ts*pulso_ancho/2
    unpulso = triangular(t_pulsoEval)
    muestras_pulso = len(t_unpulso)

    muestras_N = len(xki)
    mitad_pulso = int(muestras_pulso/2)
    pulsos_vacio = np.zeros(len(t_pulsos),dtype=float)
    xn_pulsos = np.copy(pulsos_vacio)
    xk_pulsos = []
    for j in range(0,muestras_N,1): # x(t) pulsos
        k0 = int(j*fs_veces)
        kn = k0 + muestras_pulso
        pulsoj = np.copy(pulsos_vacio)
        pulsoj[k0:kn]= pulsoj[k0:kn] + unpulso*xki[j]
        xk_pulsos.append(pulsoj)
        xn_pulsos = xn_pulsos+pulsoj
        
    return(t_pulsos,xn_pulsos,t_unpulso,unpulso,xk_pulsos)

xn_list = xn_actualiza(fs,fs_veces,xki,
                       pulso_ancho,pulso_causal)
[t_pulsos,x_pulsos,t_unpulso,unpulso,xk_pulsos] = xn_list # x[n]
    
# SALIDA
print('fs:',fs,'Hz ; Ts:',Ts,' s')
print('pulso_causal:',pulso_causal)
print('muestras_pulso:',len(t_unpulso))
print('t_unpulso:',t_unpulso)
print('muestras_tiempo:',len(t_pulsos))

# GRAFICAS  interactivas----------------------------
from matplotlib.widgets import Slider, Button, TextBox
from matplotlib.collections import LineCollection
import telg1034 as dsp
colores = ["blue", "orange", "green", "red", "purple", "brown"]

fig, [graf_t,graf_n] = plt.subplots(2,1)

t_causal = 0
if pulso_causal:
    t_causal = Ts*pulso_ancho/2
# lineas de cada pulso
muestras_n = len(tki)
tabla = []
for j in range(0,muestras_n,1):
    pulsoj = np.concatenate(([t_pulsos-t_causal],[xk_pulsos[j]]),axis=0)
    pulsoj = np.transpose(pulsoj)
    tabla.append(pulsoj)
print('tamañotabla',np.shape(tabla))

linea_pulsosk = LineCollection(tabla,color=colores,
                               linestyles='dashed')
linea_pulsosk.set_array(t_pulsos)
graf_t.add_collection(linea_pulsosk)
    
# x(t) grafico entorno
graf_t.axhline(0,color='black')
graf_t.set_xlabel('t')
graf_t.grid()
texto = titulo + ' ; f0='+str(f0)
texto = texto +' ; fs='+str(fs)
graf_t.set_title(texto)
graf_t.set_xlim([t_pulsos[0]-t_causal,t_pulsos[-1]-t_causal])
# x(t) componentes
linea_xt, = graf_t.plot(ti,xti,label='x(t)')
puntos_xn = graf_t.stem(tki,xki,label='x[n]',linefmt = 'C1:')
#linea_pulso, = graf_t.plot(t_unpulso-t_causal,unpulso,
#                           label='pulso',linestyle='dashed',lw=2)
graf_t.legend()

# x[n] grafico entorno
graf_n.axhline(0,color='black')
graf_n.set_xlabel('t_n')
graf_n.grid()
# x[n] componentes
linea_xt0, = graf_n.plot(ti_0,xti_0,linestyle='dotted',
                         label='x(t)')
puntos_xki = graf_n.stem(tkj,xki,linefmt = 'C1:') #, label='x[n]')
linea_x_pulsos, = graf_n.plot(t_pulsos,x_pulsos,label='x(t) pulsos')
graf_n.set_xlim([t_pulsos[0],t_pulsos[-1]])
graf_n.legend()

plt.tight_layout()
# plt.show()

# grafica interactiva
plt.subplots_adjust(bottom=0.25) # espacio widgets

# slider: barras para valores 
# amplitud slider [x,y,ancho,alto]
fs_donde = plt.axes([0.2, 0.10, 0.65, 0.03])
df_pasos = 5
fs_slider = Slider(fs_donde, 'fs',
                   (f0//df_pasos)*df_pasos,
                   (max([fs,10*f0])+2*df_pasos),
                   valinit = fs, valstep = df_pasos,
                   orientation='horizontal')

def grafico_actualiza(val):
    # actualiza valores x,y
    fs = fs_slider.val
    
    Ts = 1/fs # tamaño de paso con fs
    t_causal = 0
    if pulso_causal:
        t_causal = Ts*pulso_ancho/2
    
    xt_list = xt_actualiza(xt,f0,fs,fs_veces,nT,
                           pulso_ancho,pulso_causal)
    [ti,xti,tki,xki,tkj,ti_0,xti_0] = xt_list # x(t),x[n],x(t)_alias0
    dsp.stem_update(puntos_xn,tki,xki,graf_t) # x[n]
    dsp.stem_update(puntos_xki,tkj,xki,graf_n)
    linea_xt0.set_xdata(ti_0) #x(t)_alias0
    linea_xt0.set_ydata(xti_0)
    
    xn_list = xn_actualiza(fs,fs_veces,xki,pulso_ancho,pulso_causal)
    [t_pulsos,x_pulsos,t_unpulso,unpulso,xk_pulsos] = xn_list # x[n] pulsos, pulso
    linea_x_pulsos.set_xdata(t_pulsos) # x[n] pulsos
    linea_x_pulsos.set_ydata(x_pulsos)
    #linea_pulso.set_xdata(t_unpulso-t_causal) # pulso lti
    #linea_pulso.set_ydata(unpulso)

    # lineas de cada pulso
    muestras_n = len(tki)
    tabla = []
    for j in range(0,muestras_n,1):
        pulsoj = np.concatenate(([t_pulsos-t_causal],[xk_pulsos[j]]),axis=0)
        pulsoj = np.transpose(pulsoj)
        tabla.append(pulsoj)
    linea_pulsosk.set_segments(tabla)

    texto = titulo+' ; f0='+str(f0)
    texto = texto +' ; fs='+str(fs)
    graf_t.set_title(texto)

    graf_t.set_xlim([t_pulsos[0]-t_causal,t_pulsos[-1]-t_causal])
    graf_n.set_xlim([t_pulsos[0],t_pulsos[-1]])

    fig.canvas.draw_idle() # actualiza figura

# boton reinicio de gráfica
btn_rstdonde = plt.axes([0.85, 0.025, 0.1, 0.04])
btn_rst = Button(btn_rstdonde, 'Reset',
                 hovercolor='0.975')
def grafico_reinicia(event):
    fs_slider.reset()
    return()

# objetos interactivos
fs_slider.on_changed(grafico_actualiza)
btn_rst.on_clicked(grafico_reinicia)

plt.show()

[ interpola triángulo ] [ ejemplo ] [ algoritmo ] [ gráfica interactiva ]