def kernel_cell(n, m): """ Функция отдает двумерное сверточное ядро размером (m x n) на основании максимально плоского фильтра""" w_inter_freq = np.array(flattop(n)) w_inter_time = np.array(flattop(m)) kernel_in = np.ones((m, n), dtype=np.float64) for i in range(m): kernel_in[i] = w_inter_freq for i in range(n): kernel_in[:, i] = kernel_in[:, i] * w_inter_time kernel_in = kernel_in / np.sum(kernel_in) return kernel_in
def test_basic(self): assert_allclose(windows.flattop(6, sym=False), [-0.000421051, -0.051263156, 0.19821053, 1.0, 0.19821053, -0.051263156]) assert_allclose(windows.flattop(7, sym=False), [-0.000421051, -0.03684078115492348, 0.01070371671615342, 0.7808739149387698, 0.7808739149387698, 0.01070371671615342, -0.03684078115492348]) assert_allclose(windows.flattop(6), [-0.000421051, -0.0677142520762119, 0.6068721525762117, 0.6068721525762117, -0.0677142520762119, -0.000421051]) assert_allclose(windows.flattop(7, True), [-0.000421051, -0.051263156, 0.19821053, 1.0, 0.19821053, -0.051263156, -0.000421051])
def test_basic(self): assert_allclose(windows.flattop(6, sym=False), [-0.000421051, -0.051263156, 0.19821053, 1.0, 0.19821053, -0.051263156]) assert_allclose(windows.flattop(7, sym=False), [-0.000421051, -0.03684078115492348, 0.01070371671615342, 0.7808739149387698, 0.7808739149387698, 0.01070371671615342, -0.03684078115492348]) assert_allclose(windows.flattop(6), [-0.000421051, -0.0677142520762119, 0.6068721525762117, 0.6068721525762117, -0.0677142520762119, -0.000421051]) assert_allclose(windows.flattop(7, True), [-0.000421051, -0.051263156, 0.19821053, 1.0, 0.19821053, -0.051263156, -0.000421051])
def get_fft(data, num_samples): # Get the signal length n = num_samples w = np.hanning(n) w = flattop(n, False) # Get time resolution dt = 1 / float(fs) # Perform real fft fft_output = np.fft.rfft(data) # Calculate frequency bins rfreqs = np.fft.rfftfreq(n, dt) # Take only magnitude of spectrum fft_mag = np.abs(fft_output) # Double everything because of alias at Nyquist fft_mag = fft_mag * 2 / n # Scale magnitudes to log scale MAG_dB = 20 * np.log10(fft_mag / max(fft_mag)) return np.array([np.array(MAG_dB), np.array(rfreqs)])
def compute_frequency(sig, fs): N = len(sig) windowed = sig * windows.flattop(N, sym=False) X = log(abs(rfft(windowed))) hps = copy(X) for h in arange(2, 9): dec = decimate(X, h) hps[:len(dec)] += dec i_peak = argmax(hps) return fs * i_peak / N
def process(self, data): data = deepcopy(data) winData = data.data if self.windowType == FourierTransform.WindowTypes.Hann: winData *= windows.hann(len(winData), sym=False) elif self.windowType == FourierTransform.WindowTypes.Blackman: winData *= windows.blackman(len(winData), sym=False) elif self.windowType == FourierTransform.WindowTypes.Flattop: winData *= windows.flattop(len(winData), sym=False) elif self.windowType == FourierTransform.WindowTypes.Tukey: winData *= windows.tukey(len(winData), sym=False, alpha=self.alpha) data.data = np.fft.rfft(winData, axis=0, norm='ortho') data.axes[0] = np.fft.rfftfreq(len(data.axes[0]), np.mean(np.diff(data.axes[0]))) return data
def get_fft_window(window_type, window_length): # Generate the window with the right number of points window = None if window_type == "Bartlett": window = windows.bartlett(window_length) if window_type == "Blackman": window = windows.blackman(window_length) if window_type == "Blackman Harris": window = windows.blackmanharris(window_length) if window_type == "Flat Top": window = windows.flattop(window_length) if window_type == "Hamming": window = windows.hamming(window_length) if window_type == "Hanning": window = windows.hann(window_length) # If no window matched, use a rectangular window if window is None: window = np.ones(window_length) # Return the window return window
"""*. Сформувати вектор відліків часу тривалістю 10 с для частоти дискретизації 128 Гц. Сформувати сигнал послідовності прямокутних імпульсів. Сформувати відліки одної віконної функції тривалості 0.2 секунди та 2 секунди. Побудувати спектрограми сигналу. Побудувати тривимірні графіки модуля спектральної функції. """ import numpy as np import matplotlib.pyplot as plt from scipy.signal.windows import flattop from scipy.signal import square duration = 10 sample_rate = 128 time = np.linspace(0, duration, sample_rate * duration, endpoint=False) freq = np.linspace(0, sample_rate, sample_rate * duration) frequency = 40 time_shift = 10 window_duration1 = .2 window_duration2 = 2 NFFT1 = int(window_duration1 * sample_rate) NFFT2 = int(window_duration2 * sample_rate) window1 = flattop(NFFT1) window2 = flattop(NFFT2) x = square(2 * np.pi * time * frequency) X, Y = np.meshgrid(time, x) z = np.sin(np.sqrt(X**2 + Y**2)) ax = plt.axes(projection='3d') ax.plot_surface(time, x, z) plt.show()
def pre_processing(self): """ Complete various pre-processing steps for encoded protein sequences before doing any of the DSP-related functions or transformations. Zero-pad the sequences, remove any +/- infinity or NAN values, get the approximate protein spectra and window function parameter names. Parameters ---------- :self (PyDSP object): instance of PyDSP class. Returns ------- None """ #zero-pad encoded sequences so they are all the same length self.protein_seqs = zero_padding(self.protein_seqs) #get shape parameters of proteins seqs self.num_seqs = self.protein_seqs.shape[0] self.signal_len = self.protein_seqs.shape[1] #replace any positive or negative infinity or NAN values with 0 self.protein_seqs[self.protein_seqs == -np.inf] = 0 self.protein_seqs[self.protein_seqs == np.inf] = 0 self.protein_seqs[self.protein_seqs == np.nan] = 0 #replace any NAN's with 0's #self.protein_seqs.fillna(0, inplace=True) self.protein_seqs = np.nan_to_num(self.protein_seqs) #initialise zeros array to store all protein spectra self.fft_power = np.zeros((self.num_seqs, self.signal_len)) self.fft_real = np.zeros((self.num_seqs, self.signal_len)) self.fft_imag = np.zeros((self.num_seqs, self.signal_len)) self.fft_abs = np.zeros((self.num_seqs, self.signal_len)) #list of accepted spectra, window functions and filters all_spectra = ['power', 'absolute', 'real', 'imaginary'] all_windows = [ 'hamming', 'blackman', 'blackmanharris', 'gaussian', 'bartlett', 'kaiser', 'barthann', 'bohman', 'chebwin', 'cosine', 'exponential' 'flattop', 'hann', 'boxcar', 'hanning', 'nuttall', 'parzen', 'triang', 'tukey' ] all_filters = [ 'savgol', 'medfilt', 'symiirorder1', 'lfilter', 'hilbert' ] #set required input parameters, raise error if spectrum is none if self.spectrum == None: raise ValueError( 'Invalid input Spectrum type ({}) not available in valid spectra: {}' .format(self.spectrum, all_spectra)) else: #get closest correct spectra from user input, if no close match then raise error spectra_matches = (get_close_matches(self.spectrum, all_spectra, cutoff=0.4)) if spectra_matches == []: raise ValueError( 'Invalid input Spectrum type ({}) not available in valid spectra: {}' .format(self.spectrum, all_spectra)) else: self.spectra = spectra_matches[0] #closest match in array if self.window_type == None: self.window = 1 #window = 1 is the same as applying no window else: #get closest correct window function from user input window_matches = (get_close_matches(self.window, all_windows, cutoff=0.4)) #check if sym=True or sym=False #get window function specified by window input parameter, if no match then window = 1 if window_matches != []: if window_matches[0] == 'hamming': self.window = hamming(self.signal_len, sym=True) self.window_type = "hamming" elif window_matches[0] == "blackman": self.window = blackman(self.signal_len, sym=True) self.window = "blackman" elif window_matches[0] == "blackmanharris": self.window = blackmanharris(self.signal_len, sym=True) #** self.window_type = "blackmanharris" elif window_matches[0] == "bartlett": self.window = bartlett(self.signal_len, sym=True) self.window_type = "bartlett" elif window_matches[0] == "gaussian": self.window = gaussian(self.signal_len, std=7, sym=True) self.window_type = "gaussian" elif window_matches[0] == "kaiser": self.window = kaiser(self.signal_len, beta=14, sym=True) self.window_type = "kaiser" elif window_matches[0] == "hanning": self.window = hanning(self.signal_len, sym=True) self.window_type = "hanning" elif window_matches[0] == "barthann": self.window = barthann(self.signal_len, sym=True) self.window_type = "barthann" elif window_matches[0] == "bohman": self.window = bohman(self.signal_len, sym=True) self.window_type = "bohman" elif window_matches[0] == "chebwin": self.window = chebwin(self.signal_len, sym=True) self.window_type = "chebwin" elif window_matches[0] == "cosine": self.window = cosine(self.signal_len, sym=True) self.window_type = "cosine" elif window_matches[0] == "exponential": self.window = exponential(self.signal_len, sym=True) self.window_type = "exponential" elif window_matches[0] == "flattop": self.window = flattop(self.signal_len, sym=True) self.window_type = "flattop" elif window_matches[0] == "boxcar": self.window = boxcar(self.signal_len, sym=True) self.window_type = "boxcar" elif window_matches[0] == "nuttall": self.window = nuttall(self.signal_len, sym=True) self.window_type = "nuttall" elif window_matches[0] == "parzen": self.window = parzen(self.signal_len, sym=True) self.window_type = "parzen" elif window_matches[0] == "triang": self.window = triang(self.signal_len, sym=True) self.window_type = "triang" elif window_matches[0] == "tukey": self.window = tukey(self.signal_len, sym=True) self.window_type = "tukey" else: self.window = 1 #window = 1 is the same as applying no window #calculate convolution from protein sequences if self.convolution is not None: if self.window is not None: self.convoled_seqs = signal.convolve( self.protein_seqs, self.window, mode='same') / sum( self.window) if self.filter != None: #get closest correct filter from user input filter_matches = (get_close_matches(self.filter, all_filters, cutoff=0.4)) #set filter attribute according to approximate user input if filter_matches != []: if filter_matches[0] == 'savgol': self.filter = savgol_filter(self.signal_len, self.signal_len) elif filter_matches[0] == 'medfilt': self.filter = medfilt(self.signal_len) elif filter_matches[0] == 'symiirorder1': self.filter = symiirorder1(self.signal_len, c0=1, z1=1) elif filter_matches[0] == 'lfilter': self.filter = lfilter(self.signal_len) elif filter_matches[0] == 'hilbert': self.filter = hilbert(self.signal_len) else: self.filter = "" #no filter
class PyQtGraphPlotter(QtWidgets.QGroupBox): @enum.unique class WindowTypes(enum.Enum): Rectangular = 0 Hann = 1 Flattop = 2 Tukey_5Percent = 3 windowFunctionMap = { WindowTypes.Rectangular: lambda M: windows.boxcar(M, sym=False), WindowTypes.Hann: lambda M: windows.hann(M, sym=False), WindowTypes.Flattop: lambda M: windows.flattop(M, sym=False), WindowTypes.Tukey_5Percent: lambda M: windows.tukey(M, sym=False, alpha=0.05), } dataIsPower = False prevDataSet = None curDataSet = None axes_units = [ureg.dimensionless] data_unit = ureg.dimensionless def __init__(self, parent=None): _style_pg() super().__init__(parent) pal = self.palette() highlightPen = pg.mkPen( pal.color(QtGui.QPalette.Highlight).darker(120)) darkerHighlightPen = pg.mkPen(highlightPen.color().darker(120)) alphaColor = darkerHighlightPen.color() alphaColor.setAlphaF(0.25) darkerHighlightPen.setColor(alphaColor) self.toolbar = QtWidgets.QToolBar(self) self.toolbar.addWidget(QtWidgets.QLabel("Fourier transform window:")) self.windowComboBox = QtWidgets.QComboBox(self.toolbar) for e in self.WindowTypes: self.windowComboBox.addItem(e.name, e) self.toolbar.addWidget(self.windowComboBox) self.windowComboBox.currentIndexChanged.connect(self._updateFTWindow) self.pglwidget = pg.GraphicsLayoutWidget(self) self.pglwidget.setBackground(None) vbox = QtWidgets.QVBoxLayout(self) vbox.addWidget(self.toolbar) vbox.addWidget(self.pglwidget) self.plot = self.pglwidget.addPlot(row=0, col=0) self.ft_plot = self.pglwidget.addPlot(row=1, col=0) self.plot.setLabels(title="Data") self.ft_plot.setLabels(title="Magnitude spectrum") self.plot.showGrid(x=True, y=True) self.ft_plot.showGrid(x=True, y=True) self._make_plot_background(self.plot) self._make_plot_background(self.ft_plot) self._lines = [] self._lines.append(self.plot.plot()) self._lines.append(self.plot.plot()) self._lines[0].setPen(darkerHighlightPen) self._lines[1].setPen(highlightPen) self._ft_lines = [] self._ft_lines.append(self.ft_plot.plot()) self._ft_lines.append(self.ft_plot.plot()) self._ft_lines[0].setPen(darkerHighlightPen) self._ft_lines[1].setPen(highlightPen) self._lastPlotTime = time.perf_counter() def _make_plot_background(self, plot, brush=None): if brush is None: brush = pg.mkBrush(self.palette().color(QtGui.QPalette.Base)) vb_bg = QtWidgets.QGraphicsRectItem(plot) vb_bg.setRect(plot.vb.rect()) vb_bg.setBrush(brush) vb_bg.setFlag(QtWidgets.QGraphicsItem.ItemStacksBehindParent) vb_bg.setZValue(-1e9) plot.vb.sigResized.connect(lambda x: vb_bg.setRect(x.geometry())) def setLabels(self, axesLabels, dataLabel): self.axesLabels = axesLabels self.dataLabel = dataLabel self.updateLabels() def updateLabels(self): self.plot.setLabels(bottom='{} [{:C~}]'.format(self.axesLabels[0], self.axes_units[0]), left='{} [{:C~}]'.format(self.dataLabel, self.data_unit)) ftUnits = self.data_unit if not self.dataIsPower: ftUnits = ftUnits**2 self.ft_plot.setLabels(bottom='1 / {} [{:C~}]'.format( self.axesLabels[0], (1 / self.axes_units[0]).units), left='Power [dB-({:C~})]'.format(ftUnits)) def get_ft_data(self, data): delta = np.mean(np.diff(data.axes[0])) winFn = self.windowFunctionMap[self.windowComboBox.currentData()] refUnit = 1 * self.data_unit Y = np.fft.rfft(data.data / refUnit * winFn(len(data.data)), axis=0) freqs = np.fft.rfftfreq(len(data.axes[0]), delta) dBdata = 10 * np.log10(np.abs(Y)) if not self.dataIsPower: dBdata *= 2 return (freqs, dBdata) def _updateFTWindow(self): if self.prevDataSet: F, dBdata = self.get_ft_data(self.prevDataSet) self._ft_lines[0].setData(x=F, y=dBdata) if self.curDataSet: F, dBdata = self.get_ft_data(self.curDataSet) self._ft_lines[1].setData(x=F, y=dBdata) def drawDataSet(self, newDataSet, *args): plotTime = time.perf_counter() # artificially limit the replot rate to 20 Hz if (plotTime - self._lastPlotTime < 0.05): return self._lastPlotTime = plotTime self.prevDataSet = self.curDataSet self.curDataSet = newDataSet if (self.curDataSet.data.units != self.data_unit or self.curDataSet.axes[0].units != self.axes_units[0]): self.data_unit = self.curDataSet.data.units self.axes_units[0] = self.curDataSet.axes[0].units self.updateLabels() if self.prevDataSet: self._lines[0].setData(x=self._lines[1].xData, y=self._lines[1].yData) self._ft_lines[0].setData(x=self._ft_lines[1].xData, y=self._ft_lines[1].yData) if self.curDataSet: self._lines[1].setData(x=self.curDataSet.axes[0], y=self.curDataSet.data) F, dBdata = self.get_ft_data(self.curDataSet) self._ft_lines[1].setData(x=F, y=dBdata)
def testbench(): #Parametros del muestreo N = np.power(2, 10) fs = np.power(2, 10) #Size del montecarlo S = 200 #Limites de la distribucion de fr l1 = -2 * (fs / N) l2 = 2 * (fs / N) #Amplitud Ao = 2 * np.ones((S, 1), dtype=float) #Fase Po = np.zeros((S, 1), dtype=float) #Frecuencia central fo = int(np.round(N / 4)) #Cantidad ventanas Nw = 2 #Fr sera una variable aleatoria de distribucion uniforme entre -2 y 2 #Genero 200 realizaciones de fr fr = np.random.uniform(l1, l2, S).reshape(S, 1) #Genero 200 realizaciones de f1 f1 = fo + fr #Enciendo el generador de funciones generador = gen.signal_generator(fs, N) #Genero 200 realizaciones de x (t, x) = generador.sinewave(Ao, f1, Po) #Genera una matriz con las 5 ventanas w = np.array([], dtype='float').reshape(N, 0) for j in range(0, Nw): if j is 0: wj = np.ones((N, 1), dtype=float) elif j is 1: wj = win.flattop(N).reshape(N, 1) elif j is 2: wj = win.hann(N).reshape(N, 1) elif j is 3: wj = win.blackman(N).reshape(N, 1) elif j is 4: wj = win.flattop(N).reshape(N, 1) w = np.hstack([w, wj.reshape(N, 1)]) #Inicializo el analizador de espectro analizador = sa.spectrum_analyzer(fs, N, algorithm="fft") for j in range(0, Nw): wi = w[:, j].reshape(N, 1) Ao_estimador = np.array([], dtype='float').reshape(1, 0) #Contempla la energia o potencia en un ancho de banda Ao2_estimador = np.array([], dtype='float').reshape(1, 0) for i in range(0, S): xi = x[:, i].reshape(N, 1) xi = xi * wi #Obtengo el espectro para las i esima realizacion de x (f, Xi) = analizador.module(xi) aux = Xi[(fo - 2):(fo + 3)] #Calculo una realizacion del estimador Aoi_estimador = Xi[fo].reshape(1, 1) #Ao2i_estimador = np.sqrt(np.sum(np.power(aux,2))).reshape(1,1) Ao2i_estimador = np.sqrt(np.sum(np.power(aux, 2)) / 5).reshape( 1, 1) #Lo adjunto a los resultados Ao_estimador = np.hstack([Ao_estimador, Aoi_estimador]) Ao2_estimador = np.hstack([Ao2_estimador, Ao2i_estimador]) plt.figure() plt.hist(np.transpose(Ao_estimador), bins='auto') plt.axis('tight') plt.title('Energia en el bin para ventana #' + str(j)) plt.xlabel('Variable aleatoria') plt.ylabel('Probabilidad') plt.grid() plt.figure() plt.hist(np.transpose(Ao2_estimador), bins='auto') plt.title('Energia en un ancho de banda para ventana #' + str(j)) plt.axis('tight') plt.xlabel('Variable aleatoria') plt.ylabel('Probabilidad') plt.grid() sesgo = np.mean(Ao_estimador) - Ao[0, 0] varianza = np.mean(np.power(Ao_estimador - np.mean(Ao_estimador), 2)) sesgo2 = np.mean(Ao2_estimador) - Ao[0, 0] varianza2 = np.mean(np.power(Ao2_estimador - np.mean(Ao2_estimador), 2)) print('------------Ventana #' + str(j) + '--------------') print('Estimador: Energia en un bin') print('Sesgo: ' + str(sesgo)) print('Varianza: ' + str(varianza)) print('Estimador: Energia en un ancho de banda (5 bin)') print('Sesgo: ' + str(sesgo2)) print('Varianza: ' + str(varianza2)) i = i + 1 return w
class MPLCanvas(QtWidgets.QGroupBox): """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" @enum.unique class WindowTypes(enum.Enum): Rectangular = 0 Hann = 1 Flattop = 2 Tukey_5Percent = 3 windowFunctionMap = { WindowTypes.Rectangular: lambda M: windows.boxcar(M, sym=False), WindowTypes.Hann: lambda M: windows.hann(M, sym=False), WindowTypes.Flattop: lambda M: windows.flattop(M, sym=False), WindowTypes.Tukey_5Percent: lambda M: windows.tukey(M, sym=False, alpha=0.05), } dataIsPower = False dataSet = None prevDataSet = None _prevAxesLabels = None _axesLabels = None _prevDataLabel = None _dataLabel = None _lastPlotTime = 0 _isLiveData = False def __init__(self, parent=None): style_mpl() super().__init__(parent) dpi = QtWidgets.qApp.primaryScreen().logicalDotsPerInch() self.fig = Figure(dpi=dpi) self.fig.patch.set_alpha(0) self.axes = self.fig.add_subplot(2, 1, 1) self.ft_axes = self.fig.add_subplot(2, 1, 2) self.canvas = FigureCanvasQTAgg(self.fig) self.mpl_toolbar = NavigationToolbar2QT(self.canvas, self) self.mpl_toolbar.addSeparator() self.autoscaleAction = self.mpl_toolbar.addAction("Auto-scale") self.autoscaleAction.setCheckable(True) self.autoscaleAction.setChecked(True) self.autoscaleAction.triggered.connect(self._autoscale) self.mpl_toolbar.addWidget( QtWidgets.QLabel("Fourier transform " "window: ")) self.windowComboBox = QtWidgets.QComboBox(self.mpl_toolbar) for e in MPLCanvas.WindowTypes: self.windowComboBox.addItem(e.name, e) self.mpl_toolbar.addWidget(self.windowComboBox) self.windowComboBox.currentIndexChanged.connect(self._replot) vbox = QtWidgets.QVBoxLayout(self) vbox.addWidget(self.mpl_toolbar) vbox.addWidget(self.canvas) vbox.setContentsMargins(0, 0, 0, 0) vbox.setStretch(0, 1) vbox.setStretch(1, 1) self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.updateGeometry() self.fig.tight_layout() self._lines = self.axes.plot([], [], [], [], animated=True) self._lines[0].set_alpha(0.25) self._ftlines = self.ft_axes.plot([], [], [], [], animated=True) self._ftlines[0].set_alpha(0.25) self.axes.legend(['Previous', 'Current']) self.ft_axes.legend(['Previous', 'Current']) self.axes.set_title('Data') self.ft_axes.set_title('Fourier transformed data') self._redraw() # Use a timer with a timeout of 0 to initiate redrawing of the canvas. # This ensures that the eventloop has run once more and prevents # artifacts. self._redrawTimer = QtCore.QTimer(self) self._redrawTimer.setSingleShot(True) self._redrawTimer.setInterval(100) self._redrawTimer.timeout.connect(self._redraw) # will be disconnected in drawDataSet() when live data is detected. self._redraw_id = self.canvas.mpl_connect('draw_event', self._redraw_artists) def _redraw_artists(self, *args): if not self._isLiveData: self.axes.draw_artist(self._lines[0]) self.ft_axes.draw_artist(self._ftlines[0]) self.axes.draw_artist(self._lines[1]) self.ft_axes.draw_artist(self._ftlines[1]) def _redraw(self): self.fig.tight_layout() self.canvas.draw() self.backgrounds = [ self.fig.canvas.copy_from_bbox(ax.bbox) for ax in (self.axes, self.ft_axes) ] self._redraw_artists() def showEvent(self, e): super().showEvent(e) self._redrawTimer.start() def resizeEvent(self, e): super().resizeEvent(e) self._redrawTimer.start() def get_ft_data(self, data): delta = np.mean(np.diff(data.axes[0])) winFn = self.windowFunctionMap[self.windowComboBox.currentData()] refUnit = 1 * data.data.units Y = np.fft.rfft(np.array(data.data / refUnit) * winFn(len(data.data)), axis=0) freqs = np.fft.rfftfreq(len(data.axes[0]), delta) dBdata = 10 * np.log10(np.abs(Y)) if not self.dataIsPower: dBdata *= 2 data_slice = np.array(freqs) < 2.1 return (freqs[data_slice], dBdata[data_slice]) def _dataSetToLines(self, data, line, ftline): if data is None: line.set_data([], []) ftline.set_data([], []) return #data.data -= np.mean(data.data) line.set_data(data.axes[0], data.data) freqs, dBdata = self.get_ft_data(data) ftline.set_data(freqs, dBdata) def _autoscale(self, *, redraw=True): prev_xlim = self.axes.get_xlim() prev_ylim = self.axes.get_ylim() prev_ft_xlim = self.ft_axes.get_xlim() prev_ft_ylim = self.ft_axes.get_ylim() self.axes.relim() self.axes.autoscale() self.ft_axes.relim() self.ft_axes.autoscale() need_redraw = (prev_xlim != self.axes.get_xlim() or prev_ylim != self.axes.get_ylim() or prev_ft_xlim != self.ft_axes.get_xlim() or prev_ft_ylim != self.ft_axes.get_ylim()) if need_redraw and redraw: self._redraw() return need_redraw def _replot(self, redraw_axes=False, redraw_axes_labels=False, redraw_data_label=False): if not self._isLiveData: self._dataSetToLines(self.prevDataSet, self._lines[0], self._ftlines[0]) self._dataSetToLines(self.dataSet, self._lines[1], self._ftlines[1]) if self._axesLabels and redraw_axes_labels: self.axes.set_xlabel('{} [{:C~}]'.format( self._axesLabels[0], self.dataSet.axes[0].units)) self.ft_axes.set_xlabel('1 / {} [1 / {:C~}]'.format( self._axesLabels[0], self.dataSet.axes[0].units)) if self._dataLabel and redraw_data_label: self.axes.set_ylabel('{} [{:C~}]'.format(self._dataLabel, self.dataSet.data.units)) ftUnits = self.dataSet.data.units if not self.dataIsPower: ftUnits = ftUnits**2 self.ft_axes.set_ylabel('Power [dB-({:C~})]'.format(ftUnits)) axis_limits_changed = False if (self.autoscaleAction.isChecked()): axis_limits_changed = self._autoscale(redraw=False) # check whether a full redraw is necessary or if simply redrawing # the data lines is enough if (redraw_axes or redraw_axes_labels or redraw_data_label or axis_limits_changed): self._redraw() else: for bg in self.backgrounds: self.canvas.restore_region(bg) self._redraw_artists() self.canvas.blit(self.axes.bbox) self.canvas.blit(self.ft_axes.bbox) def drawDataSet(self, newDataSet, axes_labels, data_label): plotTime = time.perf_counter() looksLikeLiveData = plotTime - self._lastPlotTime < 1 if looksLikeLiveData != self._isLiveData: if looksLikeLiveData: self.canvas.mpl_disconnect(self._redraw_id) else: self._redraw_id = self.canvas.mpl_connect( 'draw_event', self._redraw_artists) self._isLiveData = looksLikeLiveData # artificially limit the replot rate to 5 Hz if (plotTime - self._lastPlotTime < 0.2): return self._lastPlotTime = plotTime self.prevDataSet = self.dataSet self.dataSet = newDataSet redraw_axes = (self.prevDataSet is None or len(self.prevDataSet.axes) != len(self.dataSet.axes)) if not redraw_axes: for x, y in zip(self.prevDataSet.axes, self.dataSet.axes): if x.units != y.units: redraw_axes = True break redraw_axes_labels = ( self._axesLabels != axes_labels or self.prevDataSet and self.dataSet and self.prevDataSet.axes[0].units != self.dataSet.axes[0].units) redraw_data_label = ( self._dataLabel != data_label or self.prevDataSet and self.dataSet and self.prevDataSet.data.units != self.dataSet.data.units) self._axesLabels = axes_labels self._dataLabel = data_label self._replot(redraw_axes, redraw_axes_labels, redraw_data_label)
duration = 1 sample_rate = 128 time = np.arange(0, duration, 1 / sample_rate) freq = np.arange(0, sample_rate, 1 / duration) frequency = [2, 2.5] title = [["Сигнал з частотою 2 Гц", "Сигнал з частотою 2.5 Гц"], ['Амплітудний спектр без віконної функції', 'Амплітудний спектр без віконної функції'], ['Амплітудний спектр із віконною функцією', 'Амплітудний спектр із віконною функцією']] x_label = ["Час, с", "Частота, Гц", "Частота, Гц"] figure, axes = plt.subplots(2, 3, constrained_layout=True) figure.set_size_inches(12, 6) for i, ax in enumerate(axes): x = np.sin(2 * np.pi * frequency[i] * time) y = 2 * np.abs(fft(x) / len(x)) y_window = 2 * np.abs(fft(x * flattop(len(x))) / len(x)) ax[0].plot(time, x) ax[1].stem(freq, y) ax[1].set_xlim(0, sample_rate / 2) ax[2].stem(freq, y_window) ax[2].set_xlim(0, sample_rate / 2) for j in range(3): ax[j].set_xlabel(x_label[j]) ax[j].set_title(title[j][i]) ax[j].set_ylabel("Амплітуда") ax[j].minorticks_on() ax[j].grid(which='major', linewidth=1.2) ax[j].grid(which='minor', linewidth=.5) plt.show()