1.3 Señal – Suma por Fasores de igual frecuencia con Sympy-Python

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]
..


1. Suma por Fasores

Referencia: McClellan 2.6 p48

Con señales de igual frecuencia en el mismo medio de transmisión las señales se suman. Una forma simplificada de sumar señales con diferente amplitud, fase e igual frecuencia es por medio de fasores , considerando:

x(t) = \sum_{k=1}^{N} A_k \cos(\omega_0 t + \varphi_k) = A \cos(\omega_0 t + \varphi)

Se puede representar los cosenos en forma compleja

A \cos(\omega_0 t + \varphi) = \Re \Big\{ A e^{j(\omega_0 t + \varphi)} \Big\} = \Re \Big \{ A e^{j\varphi} e^{j \omega_0 t} \Big\} \sum_{k=1}^{N} A_k e^{j\varphi_k} = A e^{j \varphi}

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]
..


2. Ejemplo – regla de suma de fasores

Referencia: McClellan 2.6.3 p51

Sumar dos señales sinusoidales de igual frecuencia usando fasores:

x_1 (t) = 1.7\cos(20 \pi t + 70\pi / 180) x_2 (t) = 1.9\cos(20 \pi t + 200\pi / 180)

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]
..


3. Desarrollo analítico

La frecuencia es 10 Hz para ambas señales.
Las suma de x1(t) + x2(t) mediante fasores tiene cuatro pasos:

1. cada señal se escribe usando fasores:

X_1 = 1.7e^{j 70\pi / 180} X_2 = 1.9e^{j 200\pi / 180}

2. cada fasor se escribe en forma compleja (rectangular):

X_1 = 0.5814 + j 1.5975 X_2 = -1.7854 - j 0.6498

3. se suman las partes reales e imaginarias

X_3 = X_1 + X_2 = (0.5814 + j 1.5975) + (-1.7854 - j 0.6498) = -1.204 + j 0.9477

4.Se reescribe a la forma polar y luego a senoidal con la frecuencia de las señales

X_3 = 1.5322 e^{j 141.79\pi/180} x_3(t) = 1.5322\cos(20\pi t +141.79\pi /180)

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]
..


4. Algoritmo con Sympy

Las señales en el bloque de ingreso son:

x1 = 1.7*sym.cos(20*pi*t+70*pi/180)
x2 = 1.9*sym.cos(20*pi*t+200*pi/180)

Desarrollando solo para x1(t) se obtienen los parámetros de un término coseno,

x1_args = dsp.cos_args_one_term(x1)
amplitud = x1_args['amplitud']
fase = x1_args['fase']

El fasor se obtiene escribiendo las expresiones con amplitud y fase,

fasor1 = amplitud*sym.exp(I*fase)

para la forma de números complejos, se usa sym.expand(fasor1,complex=True),

>>> fasor1
1.7*exp(7*I*pi/18)
>>> sym.expand(fasor1,complex=True)
1.7*cos(7*pi/18) + 1.7*I*sin(7*pi/18)
>>> sym.expand(fasor1,complex=True).evalf()
0.581434243653637 + 1.59747745533604*I
>>> 

sin embargo la expresión muestra el cos() para la parte real y sin() para la imaginaria. La expresión las evalúa la funciones senoidales añadiendo .evalf() al final de la instrucción, quedando la instrucción de la siguiente forma:

fasor1_complex = sym.expand(fasor1,complex=True).evalf()

El proceso se repite para la señal x2(t) para luego sumar los fasores en forma compleja, siempre y cuando las frecuencias sean iguales.

Para mantener la amplitud del fasor_suma con signo positivo, evitando que Sympy simplifique la fase con π se usa la instrucción sym.UnevaluatedExpr(fase).

El resultado del algoritmo se muestra a continuación:

fasor1: 1.7*exp(7*I*pi/18)
fasor1_complex: 0.581434243653637 + 1.59747745533604*I
fasor2: 1.9*exp(-8*I*pi/9)
fasor2_complex: -1.78541597949323 - 0.649838272318771*I
fasor_suma_complex: -1.20398173583959 + 0.947639183017274*I
fasor_suma: 1.53218538089389*exp(-0.666817831701547 + pi)
x_suma: 1.53218538089389*cos(10*t*(2*pi) - (0.666817831701547 + pi))
>>>

Las instrucciones en Python son:

# ejemplo 2.6.3 p52 Suma de Fasores y graf_polar
# 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

# INGRESO
x1 = 1.7*sym.cos(20*pi*t+70*pi/180)
x2 = 1.9*sym.cos(20*pi*t+200*pi/180)

# PROCEDIMIENTO
# señal x1
x1_args = dsp.cos_args_one_term(x1)
amplitud = x1_args['amplitud']
fase = x1_args['fase']
fasor1 = amplitud*sym.exp(I*fase)
# fasor números complejos
fasor1_complex = sym.expand(fasor1,complex=True).evalf()

# señal x2
x2_args = dsp.cos_args_one_term(x2)
amplitud = x2_args['amplitud']
fase = x2_args['fase']
fasor2 = amplitud*sym.exp(I*fase)
# fasor números complejos
fasor2_complex = sym.expand(fasor2,complex=True).evalf()

# suma de fasores de x1(t) y x2(t) en la misma freq
if x1_args['freq_Hz'] == x2_args['freq_Hz']:
    freq_iguales = True
    fasorsum_complex = fasor1_complex + fasor2_complex
    
    amplitud = sym.Abs(fasorsum_complex)
    fase = sym.arg(fasorsum_complex)
        
    fasor_suma = amplitud*sym.exp(sym.UnevaluatedExpr(fase))
    x_suma = amplitud*sym.cos(DosPi*x1_args['freq_Hz']*t+sym.UnevaluatedExpr(fase))
    x_suma_args = dsp.cos_args_one_term(x_suma)
    
else: # diferentes frecuencias
    freq_iguales = False
    fasorsum_complex = 0+0j
    fasor_suma = fasor1 +fasor2
    x_suma = x1+x2

# SALIDA
print('fasor1:',fasor1)
print('fasor1_complex:',fasor1_complex)
print('fasor2:',fasor2)
print('fasor2_complex:',fasor2_complex)
if freq_iguales:
    print('fasor_suma_complex:',fasorsum_complex)
    print('fasor_suma:',fasor_suma)
else:
    print('  las frecuencias no son iguales en x1(t) y x2(t)')
print('x_suma:',x_suma)

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]
..


5. Gráfica de fasores

La gráfica de fasores es de tipo polar con flechas que parten desde el centro y se dibujan usando la amplitud y fase por cada fasor.

Primero se genera la lista de x_amplitud y x_fase, la suma se añade solamente si las señales son de igual frecuencia.

La forma polar de la gráfica se indica con projection='polar' al definir el sub-gráfico. Como referencia se añade puntos al final de cada fasor y se diferencian al usar un color diferente.

La instrucción de flechas, requiere el punto de partida (0,0) y el punto de llegada para cada fasor (x_fase[i],x_amplitud[i]).

Las etiquetas del gráfico se muestran en radianes
Suma fasores igual frecuencia 01

Instrucciones adicionales para la gráfica polar

# GRAFICA de suma de fasores
x_etiqueta = ['x1(t)','x2(t)']
x_amplitud = [float(x1_args['amplitud']),
              float(x2_args['amplitud'])]
x_fase = [float(x1_args['fase']),
          float(x2_args['fase'])]
if freq_iguales:
        x_amplitud.append(float(x_suma_args['amplitud']))
        x_fase.append(float(x_suma_args['fase']))
        x_etiqueta.append('x_suma')

# graficar
fig_polar  = plt.figure()
graf_polar = fig_polar.add_subplot(projection='polar')
punto = graf_polar.plot(x_fase,x_amplitud,'o',xunits='radians')
for i in range(0,len(x_amplitud),1):
    if (i<=9): # color de la línea
        lineacolor = 'C'+str(i)
    else:
        numcolor = i%10
        lineacolor = 'C'+str(numcolor)
    graf_polar.quiver(0,0,x_fase[i],x_amplitud[i],
                      angles='xy', scale_units='xy', scale=1,
                      label = x_etiqueta[i],
                      color = lineacolor)
# actualiza etiquetas de gráfico polar
etiquetas = ['0', '$\pi$/4', '$\pi$/2','3$\pi$/4',
             '$\pi$','5$\pi$/4','3$\pi$/2','7$\pi$/4']
dtheta = int(360/len(etiquetas))
lines, labels = plt.thetagrids(range(0, 360,dtheta),
                               etiquetas)
graf_polar.set_title('Fasores Xk a '+str(x1_args['freq_Hz'])+ ' Hz')
graf_polar.legend()
plt.show()

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]
..


6. Función

6.1 Incorporar parámetro fasor a dsp.cos_args_one_term()

El fasor puede considerarse como parte de los parámetros de una señal, por lo que el cálculo se incorpora a la instrucción dsp.cos_args_one_term() como parámetro: 'fasor' y 'fasor_complex'.

La disponibilidad del parámetro 'fasor' puede facilitar las operaciones de suma de señales de igual frecuencia cuando se usen mas componentes de la señal. La simplificación del algoritmo se muestra en el siguiente ejercicio:

6.2 Ejercicio

Referencia: McClellan ejercicio 2.8 p54

Considere las dos señales:

x_1 (t) = 5\cos(2 \pi (100) t + \pi /3) x_2 (t) = 4\cos(2 \pi(100) t + \pi / 4)

6.3 Algoritmo en Python

Considerando que se pueden usar mas señales de igual frecuencia para sumar los fasores, se procede a usar x_senales como una lista de señales a ser analizada, junto a x_etiqueta con las etiquetas para las gráficas.

# INGRESO
x1 = 5*sym.cos(DosPi*100*t + pi/3)
x2 = 4*sym.cos(DosPi*100*t - pi/4)

x_senales = [x1,x2] # sumar fasores de igual frecuencia
x_etiqueta = ['x1(t)','x2(t)']

La revisión de las frecuencias de cada señal se realiza con el diccionario x_freq que tendra el 'conteo' de señales con la misma frecuencia y 'fasor_complex' que se acumulará cada vez que se encuentre una señal de igual frecuencia en la lista x_senales.

Luego se realiza una revisión de las frecuencias que tienen conteo>1 para generar la senal x_suma y añadir a la lista x_senales con la etiqueta correspondiente en x_etiqueta. Se debe actualizar el x_conteo que es usado para enumerar las gráficas.

La salida del algoritmo para el ejercicio se presenta como:

freq_Hz: 100
  conteo : 2
  fasor_complex : 5.32842712474619 + 1.501699894176*I
  senal : 5.53599477925144*cos(100*t*(2*pi) + 0.27470298976466)

Las simplificación del algoritmo usando la función actualizada y las señales en una lista se muestran a continuación:

Instrucciones en Python.

# ejercicio 2.8 p54 Suma de Fasores y graf_polar
# 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

# INGRESO
x1 = 5*sym.cos(DosPi*100*t + pi/3)
x2 = 4*sym.cos(DosPi*100*t - pi/4)

x_senales = [x1,x2] # sumar fasores de igual frecuencia
x_etiqueta = ['x1(t)','x2(t)']

# PROCEDIMIENTO
x_conteo = len(x_senales)
# sumar fasores por frecuencias iguales
x_freq = {}
for unasenal in x_senales:
    xk_args = dsp.cos_args_one_term(unasenal)    
    freq_k = xk_args['freq_Hz']
    fasor_complex_k = xk_args['fasor_complex']
    if freq_k in x_freq: # sumar fasores en la freq_k
        x_freq[freq_k]['conteo'] = x_freq[freq_k]['conteo'] + 1
        fasor_complex = x_freq[freq_k]['fasor_complex']
        x_freq[freq_k]['fasor_complex'] = fasor_complex + fasor_complex_k
    else: # primer fasor en freq_k
        x_freq[freq_k] = {}
        x_freq[freq_k]['conteo'] = 1
        x_freq[freq_k]['fasor_complex'] = fasor_complex_k

# señal x_suma(t) por frecuencias 
for unafreq in x_freq:
    if x_freq[unafreq]['conteo']>1: # existe x_suma
        fasor_complex = x_freq[unafreq]['fasor_complex']
        amplitud = sym.Abs(fasor_complex)
        fase = sym.arg(fasor_complex)
        fasor_suma = amplitud*sym.exp(sym.UnevaluatedExpr(fase))
        x_suma = amplitud*sym.cos(DosPi*unafreq*t+sym.UnevaluatedExpr(fase))
        x_freq[unafreq]['senal'] = x_suma
        # actualiza lista de señales
        x_senales.append(x_suma)
        x_etiqueta.append('x_'+str(unafreq)+'Hz')
        x_conteo = len(x_senales)

# SALIDA
for unafreq in x_freq:
    print('freq_Hz:',unafreq)
    for unparametro in x_freq[unafreq]:
        print(' ',unparametro,':', x_freq[unafreq][unparametro])

6.4 funciones para gráficas

En el desarrollo de las gráficas una operación recurrente es encontrar el periodo máximo T_max, por lo que periodo_minmax(x_freq) que se incorpora a telg1034.py.

De forma semejante se requiere un color diferente por cada componente de señal en x_senales dado por su índice en la lista con la función auxiliar . De esa forma se facilita la identificación entre la gráfica de fasores y la gráfica de tiempo _graf_lineacolor_i(i).

Para las gráficas de la suma de fasores y señales en tiempo se requiere realizar las listas de parámetros para cada gráfica.

Se destaca la relación entre las gráfica de fasores y en tiempo al rotar graf_polar como se muestra en la imagen.

grafica suma fasores 02

Instrucciones adicionales para las gráficas

# GRAFICA de suma de fasores ---------
# PROCEDIMIENTO de suma de fasores
x_amplitud = [] ; x_fase = [] ; x_ceros = []
for unasenal in x_senales:
    parametro = dsp.cos_args_one_term(unasenal)
    freq_k = parametro['freq_Hz']
    ampl_k = parametro['amplitud']
    fase_k = parametro['fase']
    x_amplitud.append(float(ampl_k))
    x_fase.append(float(fase_k))
    # marca un cero cos(wt+p)=0; wt+p=pi/2
    # t_cero = (pi/2-p)/w = (pi/2-p)/(2*pi*f)
    uncero = sym.nan # si es una constante
    if abs(freq_k)>0:
        uncero = (pi/2 - fase_k)/(2*pi*freq_k)
        uncero = np.around(float(uncero),decimals=15)
    x_ceros.append(uncero)

# graficar
def _graf_lineacolor_i(i):
    ''' función auxiliar, asigna el color en la paleta de matplotlib
    acorde al número i entre 0 y 9
    '''
    if (i<=9): # color de la línea
        lineacolor = 'C'+str(i)
    else:
        numcolor = i%10
        lineacolor = 'C'+str(numcolor)
    return(lineacolor)

fig_polar  = plt.figure()
graf_polar = fig_polar.add_subplot(projection='polar')
punto = graf_polar.plot(x_fase,x_amplitud,'o',xunits='radians')
for i in range(0,len(x_amplitud),1):
    graf_polar.quiver(0,0,x_fase[i],x_amplitud[i],
                      angles='xy', scale_units='xy', scale=1,
                      label = x_etiqueta[i],
                      color = _graf_lineacolor_i(i))
# actualiza etiquetas de gráfico polar
etiquetas = ['0', '$\pi$/4', '$\pi$/2','3$\pi$/4',
             '$\pi$','5$\pi$/4','3$\pi$/2','7$\pi$/4']
dtheta = int(360/len(etiquetas))
lines, labels = plt.thetagrids(range(0, 360,dtheta),
                               etiquetas)
graf_polar.set_title('Fasores Xk')
graf_polar.legend()
#plt.show()


# GRAFICAR x(t) ---------------------------
# INGRESO
tramos_T = 20       # tramos por periodo
periodos_graf = 2   # periodos en una grafica

# PROCEDIMIENTO GRAFICA
# periodo máximo y mínimo de señales
def periodo_minmax(x_freq):
    ''' función auxiliar: obtiene T_max y T_min
    de una lista de frecuencias x_freq
    ingresa: x_freq
    salida:  [T_max,Tmin].
    Si la frecuencia es cero, T_max=1,T_min=1
    '''
    T_max = 1 ; T_min = 1
    freq = np.array(list(x_freq))
    freq_min = float(np.min(np.abs(freq)))
    freq_max = float(np.max(np.abs(freq)))
    d_freq = np.abs(freq_max - freq_min) # ancho de banda
    if freq_max>0:
        T_min = 1/freq_max
    if d_freq>0: # min y max no son iguales
        if freq_min>0:
            T_max = 1/d_freq
        else:  # freq_min==0, selecionar otro T_max
            freq_min = float(np.min(freq[freq>0]))
            T_max = 1/freq_min 
    else: # min y max son iguales
        T_max = 1/freq_min
    T_minmax = [T_min,T_max] 
    return(T_minmax)

[T_min,T_max] = periodo_minmax(x_freq)

muestras_T = tramos_T + 1
# intervalo de t entre [a,b] en pasos dt
a = 0
b = T_max*periodos_graf
dt = T_min/tramos_T # tamaño de paso,tramo, muestreo
ti = np.arange(a,b+dt,dt)

xi_k = [] # muestras de cada señal x
for unasenal in x_senales:
    # x(t) a forma numérica lambda t:
    if unasenal.has(t):
        xk = sym.lambdify(t,unasenal)
    else: # es constante
        xk = lambda t: unasenal + 0*t
    xki = xk(ti) # evalúa en intervalo
    xi_k.append(np.copy(xki))

# graficar
fig_x = plt.figure()
graf_x = fig_x.add_subplot()
for i in range(0,x_conteo,1):
    color_i = _graf_lineacolor_i(i)
    graf_x.plot(ti,xi_k[i], color=color_i,
                label=x_etiqueta[i])
    graf_x.axhline(0,color='black')
    if x_ceros[i]!=sym.nan:
        graf_x.axvline(x_ceros[i],color=color_i,
                       linestyle='dashed')
graf_x.set_title('Señales xk(t)')
graf_x.set_xlabel('t (segundos)')
graf_x.set_ylabel('x_senales')
graf_x.grid()
graf_x.legend()
plt.show()

[ fasores ] [ ejemplo ] [ analítico ] [ algoritmo ] [ gráfica] [ función ]