Some of my works and projects in data science and signal processing
Utilizando la libraría pyqtgraph, generamos una animación de una onda senoidal impactando contra una discontinuidad caracterizada por su impedancia de borde. En el Repositorio hay 4 archivos para 4 diferentes versiones de impedancia de borde:
En este caso, también implementa un objeto donde se calcula la impedancia del material absorbente, de acuerdo al modelo de Delany-Bazley
Impedancia infinita
Impedancia Nula
Impedancia de un material absorbente
Impedancia de radiación
En cada uno de los casos, se importan las librerías
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
import sys
from fractions import Fraction as frac
Luego se define un objeto, llamado Plot2D, en cuyo método init se configura la ventana de ploteo, los gráficos iniciales (llamados canvas), donde se cargan unas imágenes para ilustrar la condición de borde, se configuran ejes, unidades, etc.
class Plot2D(object):
def __init__(self):
## Imagen de fondo
self.img = QtGui.QImage('002Zinf.png')
try:
# Convertir la imagen en el formato requerido por Qt
self.img = self.img.convertToFormat(QtGui.QImage.Format_ARGB32_Premultiplied)
self.imgArray = pg.imageToArray(self.img, copy=True)
except AttributeError:
print ("Image not valid!")
self.fondo1 = pg.ImageItem(image = self.imgArray)
self.fondo2 = pg.ImageItem(image = self.imgArray)
self.fondo3 = pg.ImageItem(image = self.imgArray)
self.A = 1
self.R = 1
self.R_phase = 0
self.f = 1
self.traces1 = dict()
self.traces2 = dict()
self.traces3 = dict()
self.phase = 0
self.t = np.arange(-4*np.pi,0,0.01)
self.app = QtGui.QApplication([])
self.win = pg.GraphicsWindow(title = "Ondas Estacionarias - Impedancia infinita")
self.win.setBackground('w')
self.ejePresion = pg.AxisItem('left', showValues = False, text='Presion Acústica')
self.ejePresion2 = pg.AxisItem('left', showValues = False, text='Presion Acústica')
self.ejePresion3 = pg.AxisItem('left', showValues = False, text='Presion Acústica')
dictEjeX = {-3:'3\u03BB',-2.5:'2.5\u03BB',-2 :'2\u03BB',-1.5:'1.5\u03BB',-1:'\u03BB',-0.5:'0.5\u03BB'}
self.ejeLambda = pg.AxisItem('bottom', showValues= True, text= 'Fracción de Longitud de onda')
self.ejeLambda.setTicks([dictEjeX.items()])
self.ejeLambda2 = pg.AxisItem('bottom', showValues= True, text= 'Fracción de Longitud de onda')
self.ejeLambda2.setTicks([dictEjeX.items()])
self.ejeLambda3 = pg.AxisItem('bottom', showValues= True, text= 'Fracción de Longitud de onda')
self.ejeLambda3.setTicks([dictEjeX.items()])
self.canvas = self.win.addPlot(axisItems = {'left': self.ejePresion , 'bottom':self.ejeLambda})
self.canvas.addItem(self.fondo1)
self.canvas.showGrid(x = True, y = False, alpha = 0.7)
self.fondo1.setZValue(10) # Imagen delante de otras
self.fondo1.setRect(pg.QtCore.QRectF(0, -2.1, 1.75, 4.1)) # Dimensiones de imagen dentro del grafico (ajustado a mano)
self.canvas.setYRange(-2, 2, padding=0)
self.canvas.setXRange(-3.1, 1.5, padding=0)
self.canvas.getAxis('left').setLabel('Presión ')
self.win.nextRow()
self.canvas2 = self.win.addPlot(axisItems = {'left': self.ejePresion2 , 'bottom':self.ejeLambda2})
self.canvas2.showGrid(x = True, y = False, alpha = 0.7)
self.canvas2.addItem(self.fondo2)
self.fondo2.setZValue(10) # Imagen delante de otras
self.fondo2.setRect(pg.QtCore.QRectF(0, -0.1, 1.75, 5.1)) # Dimensiones de imagen dentro del grafico (ajustado a mano)
self.canvas2.setYRange(0, 5, padding=0)
self.canvas2.setXRange(-3.1, 1.5, padding=0)
self.canvas2.addLegend()
self.canvas2.getAxis('left').setLabel('Presión al cuadrado')
self.win.nextRow()
self.canvas3 = self.win.addPlot(axisItems = {'left': self.ejePresion3 , 'bottom':self.ejeLambda3})
self.canvas3.showGrid(x = True, y = False, alpha = 0.7)
self.canvas3.addItem(self.fondo3)
self.fondo3.setZValue(10) # Imagen delante de otras
self.fondo3.setRect(pg.QtCore.QRectF(0, -2.1, 1.75, 4.2)) # Dimensiones de imagen dentro del grafico (ajustado a mano)
self.canvas3.setYRange(-2 , 2, padding=0)
self.canvas3.setXRange(-3.1, 1.5, padding=0)
self.canvas3.getAxis('bottom').setLabel('Distancia a la discontinuidad')
self.canvas3.getAxis('left').setLabel('Velocidad')
Los otros métodos del programa son:
animation inicia el timer y lo configura para que cada un intervalo de 75 mS se ejecute el método update. Luego ejecuta el método start
def animation(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.update)
timer.start(75)
self.start()
start inicia la aplicación (tomado de la documentación de pytqgraph)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
update es el método que se ejecuta cada 75 ms. Define las señales seno y coseno. Cambia la fase en cada repetición y carga los valores en tres diccionarios (uno por gráfico), llamado trace, trace2, trace3.
def update(self):
s = self.A*np.sin(2 * np.pi * self.f*(-self.t) + self.phase )
c = self.A*self.R*np.sin(2 * np.pi * self.f* self.t + self.phase + self.R_phase)
sume = s + c
s2 = np.power(s, 2)
c2 = np.power(c, 2)
sume2 = np.power(sume, 2)
vel_inc=self.A*np.cos(2 * np.pi * self.f*(-self.t+10) + self.phase)
vel_ref=self.A*-self.R*np.cos(2 * np.pi * self.f* self.t + self.phase)
vel_sum = vel_inc+vel_ref
self.trace("inc", self.t, s)
self.trace("ref", self.t, c)
self.trace("sum", self.t, sume)
self.trace2("inc", self.t, s2)
self.trace2("ref", self.t, c2)
self.trace2("sum", self.t, sume2)
self.trace3("inc", self.t, vel_inc)
self.trace3("ref", self.t, vel_ref)
self.trace3("sum", self.t, vel_sum)
self.phase += 0.1
trace son tres métodos, que leen la información del diccionario y la cargan en los ejes, actualizando la info cada 75 ms y generando la sensación de movilidad.
def trace(self,name,dataset_x,dataset_y):
if name in self.traces1:
self.traces1[name].setData(dataset_x,dataset_y)
elif name == "inc":
self.traces1[name] = self.canvas.plot(pen=pg.mkPen('r', width=2, style=QtCore.Qt.DashLine), name="Incidente")
elif name == "ref":
self.traces1[name] = self.canvas.plot(pen=pg.mkPen('g', width=2, style=QtCore.Qt.DashLine),name="Reflejada")
elif name == "sum":
self.traces1[name] = self.canvas.plot(pen=pg.mkPen('k', width=2.5),name="Suma")
def trace2(self,name,dataset_x,dataset_y):
if name in self.traces2:
self.traces2[name].setData(dataset_x,dataset_y)
elif name == "inc":
self.traces2[name] = self.canvas2.plot(pen=pg.mkPen('r', width=2, style=QtCore.Qt.DashLine), name = "Incidente" )
elif name == "ref":
self.traces2[name] = self.canvas2.plot(pen=pg.mkPen('g', width=2, style=QtCore.Qt.DashLine), name = "Reflejada")
elif name == "sum":
self.traces2[name] = self.canvas2.plot(pen=pg.mkPen('k', width=2.5), name = "Suma")
def trace3(self,name,dataset_x,dataset_y):
if name in self.traces3:
self.traces3[name].setData(dataset_x,dataset_y)
elif name == "inc":
self.traces3[name] = self.canvas3.plot(pen=pg.mkPen('r', width=2, style=QtCore.Qt.DashLine) )
elif name == "ref":
self.traces3[name] = self.canvas3.plot(pen=pg.mkPen('g', width=2, style=QtCore.Qt.DashLine))
elif name == "sum":
self.traces3[name] = self.canvas3.plot(pen=pg.mkPen('k', width=2.5))
Por último, se instancia un objeto Plot2D() y se llama a animation para comenzar
p = Plot2D()
p.animation()