2.2 Espectro x[n] – Señal discreta en tiempo

[ espectro_n ] [ algoritmo ] [ gráfica ]
..


1. Espectro de frecuencias de x[n]

Referencia: McClellan 4.1.4 p129

De forma semejante al Espectro para Operaciones en dominio de tiempo y frecuencia, se aplica el concepto de frecuencias en radiantes. El espectro permitirá analizar las señales alias: alias principal junto a otros alias generados con ±2π en cada frecuencia. Por ejemplo, para:

x1[n] = cos(0.4π n)

Es señal básica con frecuencia en radianes en 0.4π, sería alias principal, con componentes en ±0.4π. En el dominio discreto ‘n‘, las señales alias se forman con cada componente desplazados en 2πk a la derecha y -2πk a la izquierda. Siendo k un número entero. Para k=1.

Señal / alias principal otros alias, k=1
ω1[n] = 0.4π ω3[n] = 0.4π + 2π = 2.4
ω4[n] = 0.4π – 2π = -1.6
ω2[n] = – 0.4π ω5[n] = -0.4π + 2π = 1.6
ω6[n] = -0.4π – 2π = -2.4

el espectro con señales alias para k=1 es:

espectro n coseno

[ espectro_n ] [ algoritmo ] [ gráfica ]

..


2. Algoritmo en Python

El punto de partida es el algoritmo usado en Espectro para Operaciones en dominio de tiempo y frecuencia, donde las operaciones son semejantes y aplicadas al dominio ‘t‘, que deben cambiarse al domino ‘n‘. Se aplica un cambio de variable y una bandera ‘esdiscreta‘ para ajustar el eje de frecuencias a radianes en el análisis de la función cos_spectrum_list(x_senales) del archivo telg1034.py. con lo que se logra obtener básicamente los resultados del espectro ajustados al nuevo dominio

    #...
    for i in range(0,x_conteo,1):
        unasenal = x_senales[i]
        
        # revisa si unasenal es discreta con n
        esdiscreta = False
        varindepend = unasenal.free_symbols
        if (n in varindepend) and not(t in varindepend):
            unasenal = unasenal.subs(n,t)
            esdiscreta = True
        # analiza unasenal con t
        #...

cuando la señal es discreta, las frecuencias consideran el factor 2π, que se separaba cuando se calculaban en Hz.

            if esdiscreta:
                freq_k = freq_k*2*pi
            x_freq_spectr.append(freq_k)

con lo que el algoritmo se ajusta a la variable y es funcional.

Para la sección gráfica, se puede considerar el factor π como un elemento a simplificar en el vector de frecuencias. Se revisa si existe el factor en el arreglo de frecuencias y se procede a separarlo dentro del algoritmo de espectro para t. Se actualizan los valores en los resultados.

...
# revisa factor pi en freq_n
unfactor = sym.S.One ; factor_pi = False
freqn_conteo = len(freq_n)
for i in range(0,freqn_conteo,1):
    if freq_n[i].has(pi):
        factor_pi = True
if factor_pi:
    freq_n = np.array(freq_n/pi,dtype=float)
    unfactor = pi
# actualiza espectro de señal con factor pi
un_espectro_n['freq_factor'] = unfactor
un_espectro_n['freq'] = freq_n
...

2.1 Revisión de señales «alias» en x_senales en algoritmo para espectro

Para tener un marcador de frecuencias alias en las señales analizadas, se compara cada frecuencia del espectro para diferentes valores de k en la operación ±2π, mientras no sobrepase la frecuencia máxima del intervalo de freq.

...
# aliasing, Revisa espectro de frecuencias 
freq_conteo = len(freq_n)
freq_alias  = np.zeros(freq_conteo,dtype=bool)
for i in range (0,freq_conteo,1):
    unafreq = freq_n[i]
    k = 1
    unalias = unafreq + k*2
    while unalias<=freq_max:
        for j in range(i+1,freq_conteo,1):
            if abs(unalias - freq_n[j])<tolera:
                if abs(freq_n[i])>abs(freq_n[j]):
                    freq_alias[i] = True
                else:
                    freq_alias[j] = True
        k = k+1
        unalias = unafreq + k*2
un_espectro_n['freq_alias'] = freq_alias
...

Se marcan las frecuencias alias en la lista freq_alias para diferenciarlas con otro color en la gráfica de espectro, obteniendo el resultado presentado en el ejercicio.

[ espectro_n ] [ algoritmo ] [ gráfica ]

2.2 Instrucciones en Python

La señal para el espectro se construye con los componentes alias presentados en la parte teórica. Se analiza solo la señal x4[n] para el espectro.

El parámetro tolera en el bloque de ingreso es la tolerancia para redondear los valores de frecuencia en la gráfica cuando tienen varios dígitos decimales.

# ejercicio 4.4 p130 Espectro de señales senoidales discretas
# telg1034 DSP fiec-espol edelros@espol.edu.ec
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
import telg1034 as dsp

# variables continuas
from telg1034 import t,A,w,f,p,pi,DosPi,I,equivalentes
# variables discretas
from telg1034 import n

# INGRESO
# usar np.pi para evitar frecuencia simplificada por sympy
x1 = sym.cos(0.4*pi*n)
x2 = sym.cos(1.6*np.pi*n)
x3 = sym.cos(2.4*np.pi*n)
xn = x1+x2+x3
x_etiqueta_n = 'x[n]'

fs = 60  # muestreo discreto, freq_sampling
fs_veces = 12 # sobremuestreo para x(t)
muestrasN = 6 + 1  # para x[n]

tolera = 1e-9 # tolerancia a error, números iguales
# PROCEDIMIENTO
# espectro x[n]
# Ref: Blog|Espectro-Operaciones en dominio de tiempo y frecuencia
x_term = dsp.x_list_term_Add(xn)
Xe_term = dsp.cos_to_euler_one_term(x_term)
x_term_expand = dsp.euler_to_cos_list(Xe_term)
un_espectro_n = dsp.cos_spectrum_list(x_term)
un_espectro_n['x_expand'] = x_term_expand
# espectro de cada señal
freq_n = un_espectro_n['freq']
ampl_n = un_espectro_n['ampl']
fase_n = un_espectro_n['fase']
etiq_n = np.array(un_espectro_n['etiq'])

# revisa factor pi en freq_n
unfactor = sym.S.One ; factor_pi = False
freqn_conteo = len(freq_n)
for i in range(0,freqn_conteo,1):
    if freq_n[i].has(pi):
        factor_pi = True
if factor_pi:
    freq_n = np.array(freq_n/pi,dtype=float)
    unfactor = pi
# actualiza espectro de señal con factor pi
un_espectro_n['freq_factor'] = unfactor
un_espectro_n['freq'] = freq_n

# ancho de banda, freq_max y freq_min
freq_posi = freq_n[freq_n>0]
freq_max = float(max(freq_posi))
freq_min = 0
if len(freq_posi)>0:
    freq_min = float(min(freq_posi))
freq_centro = (freq_max+freq_min)/2
un_espectro_n['freq_max'] = freq_max
un_espectro_n['freq_min'] = freq_min
un_espectro_n['BW'] = freq_max-freq_min

# aliasing, Revisa espectro de frecuencias 
freq_conteo = len(freq_n)
freq_alias  = np.zeros(freq_conteo,dtype=bool)
for i in range (0,freq_conteo,1):
    unafreq = freq_n[i]
    k = 1
    unalias = unafreq + k*2
    while unalias<=freq_max:
        for j in range(i+1,freq_conteo,1):
            if abs(unalias - freq_n[j])<tolera:
                if abs(freq_n[i])>abs(freq_n[j]):
                    freq_alias[i] = True
                else:
                    freq_alias[j] = True
        k = k+1
        unalias = unafreq + k*2
un_espectro_n['freq_alias'] = freq_alias

# SALIDA
print('senal[n]:  ',xn)
print(' espectro:')
for parametro in un_espectro_n:
    print(' ',parametro,':',un_espectro_n[parametro])

[ espectro_n ] [ algoritmo ] [ gráfica ]
..


3. Gráfica espectro dominio n

Se añaden la gráfica para el espectro de frecuencias ajustada al dominio n

# GRAFICAS de espectro de frecuencias discretas---------
graf_dx = 0.12
fig_espectro = plt.figure()
freq_alias_conteo = np.sum(freq_alias)
ampl_max = float(max(ampl_n))*(1+graf_dx*2)
freq_max = float(max(un_espectro_n['freq_max'],1))*(1+graf_dx/2)
# grafica
graf_fasorn = fig_espectro.add_subplot(111)
graf_fasorn.set_xlim([-freq_max,freq_max])
graf_fasorn.set_ylim([min([min(ampl_n),0]),ampl_max])
graf_fasorn.grid()
graf_fasorn.axhline(0,color='black')
graf_fasorn.axvline(0,linestyle='dotted',color='grey')
# grafica magnitud
freq_noalias = np.invert(freq_alias) 
graf_fasorn.stem(freq_n[freq_noalias],ampl_n[freq_noalias],
                 linefmt = 'C0--')
if freq_alias_conteo>0: # hay alias
    graf_fasorn.stem(freq_n[freq_alias],ampl_n[freq_alias],
                     linefmt = 'C1--', label='alias') 
if un_espectro_n['freq_factor'] != sym.S.One:
    unfactor = r'$'+sym.latex(un_espectro_n['freq_factor'])+'$'
    freq_etiq = []
    for un_num in freq_n:
        freq_etiq.append(f''+str(round(un_num,4))+unfactor)
    graf_fasorn.set_xticks(ticks=freq_n,labels=freq_etiq)
for k in range(0,len(freq_n),1): # etiquetas de fasor
    graf_fasorn.annotate(etiq_n[k],xy=(freq_n[k],ampl_n[k]),
                         xytext=(0,5),textcoords='offset points',
                         ha='center')
graf_fasorn.set_ylabel(x_etiqueta_n)
graf_fasorn.set_xlabel('freq rad')
graf_fasorn.legend()
texto = '$' + sym.latex(xn)+'$' +'  ; fs='+str(fs)
graf_fasorn.set_title(texto)

plt.show()

[ espectro_n ] [ algoritmo ] [ gráfica ]