Hi,
Forgive me if this post is in the wrong place. The code uses tkinter GUI with several plots on seperate note tabs, hence Ive posted it in GUI
Ive been using Python for about 3 weeks but have coded in C++, clipper and VB for about 30 years.
I want to write some code to read in a file of vibration data with up to 16k readings, perform an fft to convert from time based data to frequency based data so I can analyse the data for problems. Please refer to my website www.efftek.co.uk for further information on vibration analysis if you need.
I found an open source example of a program doing much of what I wanted. Basically, it talks to an arduino which is connected to a 3 axis accelerometer, takes readings and displays them. The original source code and details are here https://hackaday.io/project/12109-open-s...m-analyzer
I have used this code to learn Python and have been editing it to my own needs.
I dont want all three channels displayed on one plot as the source code has so where fig1 had three subplots - ax_11, ax_12 and ax_13, I have deleted two of the sub plots so I just have ax_11. The same for fig2, fig3 and fig4 (fig3 and fig4 are my additions to the original code)
Now when I removed ax_?3 and change the subplots to be .add_subplot(2,1,?) etc, it still works OK but when I delete plots ax_?2 and change it to .add_subplot(1,1,1), I get errors. Whilst it will start up OK and display an empty plot, when I open a file with data to be displayed, I get a 'list' object has no attribute 'plot' error for line 247
Here is the full code. In order to try this out, you will need to download the data file from here datos_3can_20-12-2016__11_39_50.txt
I'd be grateful if anyone can advise why this is happening and how to cure it. Thanks, Steve.
Forgive me if this post is in the wrong place. The code uses tkinter GUI with several plots on seperate note tabs, hence Ive posted it in GUI
Ive been using Python for about 3 weeks but have coded in C++, clipper and VB for about 30 years.
I want to write some code to read in a file of vibration data with up to 16k readings, perform an fft to convert from time based data to frequency based data so I can analyse the data for problems. Please refer to my website www.efftek.co.uk for further information on vibration analysis if you need.
I found an open source example of a program doing much of what I wanted. Basically, it talks to an arduino which is connected to a 3 axis accelerometer, takes readings and displays them. The original source code and details are here https://hackaday.io/project/12109-open-s...m-analyzer
I have used this code to learn Python and have been editing it to my own needs.
I dont want all three channels displayed on one plot as the source code has so where fig1 had three subplots - ax_11, ax_12 and ax_13, I have deleted two of the sub plots so I just have ax_11. The same for fig2, fig3 and fig4 (fig3 and fig4 are my additions to the original code)
Now when I removed ax_?3 and change the subplots to be .add_subplot(2,1,?) etc, it still works OK but when I delete plots ax_?2 and change it to .add_subplot(1,1,1), I get errors. Whilst it will start up OK and display an empty plot, when I open a file with data to be displayed, I get a 'list' object has no attribute 'plot' error for line 247
Here is the full code. In order to try this out, you will need to download the data file from here datos_3can_20-12-2016__11_39_50.txt
I'd be grateful if anyone can advise why this is happening and how to cure it. Thanks, Steve.
#!/usr/bin/env python3 ¡¡¡ OK !!!
# fft_spectrum_gui_3can_py3_02.py
# - Added velocity and envelop (using hilbert transform) plots
# 01/02/2018 - Steve Ferry.
# Program fft_spectrum_gui_3can_py3_01.py
# - Added a timeout control to a while loop.
# 12/01/2017
# Program fft_spectrum_gui_3can.py modified:
# - From Python 2.7 to Python 3.5.
# - Works with AVR program adxl335_3can_01.c
# 20/12/2016
# Program fft_spectrum_gui_3can.py
# Program fft_spectrum_gui.py modified:
# - 3 data channels (3 axes)
# 15/11/2016
# Program fft_spectrum_gui.py
# - Based on program frame_tab_plot_07.py
# - Sample acceleration data from a ADXL335 accelerometer.
# - Plot sampled data and its FFT spectrumsu
# - Save data on file and open files with saved data.
# - Serial communication with microcontroller.e
# - Serial port selection.
# - RadioButtons to select a Window function to apply.
# 13/07/2016
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from scipy import fftpack, arange, signal
import numpy as np
import tkinter as Tk
from tkinter import filedialog
from tkinter import messagebox
from tkinter.scrolledtext import ScrolledText
from tkinter import ttk
from scipy.signal import hilbert
from numpy import linalg as nl
datos_a_leer = 4096 # Amount of samples to read.
sample_rate = 5000 # Sampling frequency (SPS).
g_scale = 3.0 / 512 # +- 3g. 10 bit ADC.
max_freq = 1500 # Maximum signal frequency, X and Y axis (accelerometer).
max_freq_z = 500 # Maximum signal frequency, Z axis (accelerometer).
fftlen = 8192 # Samples to display FFT
twflen = 2048 # twf data points to display
twopi = 6.28318530717
gconst = 9806.65
g_canal_1 = [] # Global canal_1
g_canal_2 = [] # Global canal_2
g_canal_3 = [] # Global canal_3
t_timeout = 8 # Timeout time in seconds.
def simpleParse(mainString, beginString, endString):
posBeginString = mainString.find(beginString) + len(beginString)
posEndString = mainString.find(endString)
resultado = mainString[posBeginString:posEndString]
return resultado
def extraer_int_tag(datos_arch, tag):
beginString = '<' + tag + '>'
endString = '</' + tag + '>'
str_parse = simpleParse(datos_arch, beginString, endString)
str_canal = str_parse.split(',')
canal = []
n = len(str_canal)
for i in range(n):
canal.append(int(str_canal[i]))
return canal
class Application:
def __init__(self, parent):
self.parent = parent
self.frames()
self.f_saved = True # Sampled data saved
root.protocol("WM_DELETE_WINDOW", self.on_closing)
def on_closing(self):
if (self.f_saved == False):
if messagebox.askokcancel("Quit", "Sampled data not saved. Do you wanto to quit?"):
root.destroy()
else:
root.destroy()
def frames(self):
frame1 = Tk.Frame(root, bd=5, relief='raised', borderwidth=1)
frame2 = Tk.Frame(root, bd=5, relief='raised', borderwidth=1)
note = ttk.Notebook(frame2)
self.tab1 = ttk.Frame(note)
self.tab2 = ttk.Frame(note)
self.tab3 = ttk.Frame(note)
self.tab4 = ttk.Frame(note)
note.add(self.tab1, text=" Time Waveform ")
note.add(self.tab2, text=" Acceleration Spectra ")
note.add(self.tab3, text=" Velocity Spectra ")
note.add(self.tab4, text=" Demodulated Spectra ")
# Positioning
frame1.pack(side='left', fill='both', padx=5, pady=5)
frame2.pack(side='right', fill='both', expand='true')
boton_open = Tk.Button(frame1, text="Open file", command=self.open_file)
label4 = Tk.Label(frame1, text="Select Channel")
self.text_message = ScrolledText(frame1, height=10, width=20)
self.chan_var = Tk.IntVar()
self.chan_var.set(1)
channel_button1 = Tk.Radiobutton(frame1, text="Channel 1",
variable=self.chan_var, value=1, command=self.chan_sel)
channel_button2 = Tk.Radiobutton(frame1, text="Channel 2",
variable=self.chan_var, value=2, command=self.chan_sel)
channel_button3 = Tk.Radiobutton(frame1, text="Channel 3",
variable=self.chan_var, value=3, command=self.chan_sel)
channel_button4 = Tk.Radiobutton(frame1, text="Channel 4",
variable=self.chan_var, value=3, command=self.chan_sel)
label4.grid(row=4, column=0, padx=5, pady=5)
channel_button1.grid(row=5, column=0, sticky="W")
channel_button2.grid(row=6, column=0, sticky="W")
channel_button3.grid(row=7, column=0, sticky="W")
channel_button4.grid(row=8, column=0, sticky="W")
note.pack(side='top', fill='both', padx=5, pady=5)
boton_open.grid(row=13, column=0, padx=5, pady=5)
plt.ion()
# Figure 1
fig1 = Figure(figsize=(10, 9))
fig1.suptitle('Sampled signal - Acceleration')
ax_11 = fig1.add_subplot(1, 1, 1)
ax_11.set_ylabel('g')
ax_11.grid() # Shows grid.
# Figure 2
fig2 = Figure(figsize=(10, 9))
fig2.suptitle('Acceleration Spectra')
ax_21 = fig2.add_subplot(1, 1, 1)
ax_21.set_ylabel('g')
ax_21.set_xlim(xmin=0,xmax=max_freq)
ax_21.grid()
# Figure 3
fig3 = Figure(figsize=(10, 9))
fig3.suptitle('Velocity spectra')
ax_31 = fig3.add_subplot(1, 1, 1)
ax_31.set_ylabel('mm/sec')
ax_31.set_xlim(xmin=0, xmax=max_freq)
ax_31.grid()
# Figure 4
fig4 = Figure(figsize=(10, 9))
fig4.suptitle('Demodulated spectra')
ax_41 = fig4.add_subplot(1, 1, 1)
ax_41.set_ylabel('g')
ax_41.set_xlim(xmin=0, xmax=max_freq)
ax_41.grid()
# Canvas
self.canvas1 = FigureCanvasTkAgg(fig1, master=self.tab1)
self.canvas1.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas2 = FigureCanvasTkAgg(fig2, master=self.tab2)
self.canvas2.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas3 = FigureCanvasTkAgg(fig3, master=self.tab3)
self.canvas3.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas4 = FigureCanvasTkAgg(fig4, master=self.tab4)
self.canvas4.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas1._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas2._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas3._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.canvas4._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def show_message(self, text_message, message_string):
"""Shows messages on a scrollable textbox"""
text_message.config(state=Tk.NORMAL) # Enable to modify
text_message.insert(Tk.END, message_string)
text_message.config(state=Tk.DISABLED) # Disable - Read only
text_message.see("end") # Show the "end" of text
root.update_idletasks() # Needed to make message visible
def plot(self, tab1, tab2, tab3, tab4, canal_1, canal_2, canal_3, chan_no):
if chan_no == 1:
twf = canal_1
elif chan_no == 2:
twf = canal_2
else:
twf = canal_3
num_datos = len(twf)
X = range(0, num_datos, 1)
# Scale the signal in g's
for indice in X:
twf[indice] *= g_scale
# Calculates average value for each channel.
avg = 0
for indice in X:
avg += twf[indice]
avg = avg / num_datos
# print("Vdc Channel 1: {0}, Vdc Channel 2: {1}".format(vdc_canal_1, vdc_canal_2))
# Substract DC offset
for indice in X:
twf[indice] -= avg
X1 = np.linspace(0, num_datos / 5, num=num_datos) # X axis, 5000 sps, 1/5 ms.
canal_fft = twf
N = len(canal_fft) # length of the signal
M = int(N / 2)
T = 1.0 / sample_rate
y = canal_fft
w = signal.hann(N, sym=False) # Hann (Hanning) window
ax = np.abs(fftpack.fft(y * w) * (2 / N))
ax = ax[:int(N / 2)]
xx = np.linspace(0.0, 1.0 / (2.0 * T), int(N / 2))
vx = [0] * M
for i in range( 5, M):
vx[i] = ((ax[i] * gconst) / (twopi * xx[i])) #calculate velocity
analy = hilbert(canal_fft[::])
y = abs(analy[0:M])
sig_f = abs(np.abs(fftpack.rfft(y)))
sig_n = sig_f / (nl.norm(sig_f))
hx = sig_n[0:M]
hx[0] = 0
# Figure 1. twf
ax_11 = self.canvas1.figure.get_axes()
ax_11.clear()
ax_11.plot(X1[0:twflen], twf[0:twflen])
ax_11.set_ylabel('g')
ax_11.grid() # Shows grid.
# Figure 2. FFT from signals.
ax_21 = self.canvas2.figure.get_axes()
ax_21.clear()
ax_21.plot(xx, ax)
ax_21.grid()
ax_21.set_ylabel('g')
ax_21.set_xlim(xmin=0, xmax=max_freq)
ax_31 = self.canvas3.figure.get_axes()
ax_31.clear()
ax_31.plot(xx, vx)
ax_31.grid()
ax_31.set_ylabel('mm/sec')
ax_31.set_xlim(xmin=0, xmax=max_freq)
ax_41 = self.canvas4.figure.get_axes()
ax_41.clear()
ax_41.plot(xx, hx)
ax_41.grid()
ax_41.set_ylabel('g')
ax_41.set_xlim(xmin=0, xmax=max_freq)
self.canvas1.draw()
self.canvas2.draw()
self.canvas3.draw()
self.canvas4.draw()
def chan_sel(self):
global g_canal_1, g_canal_2, g_canal_3
canal_1 = g_canal_1[:] # Copy list by value not by reference
canal_2 = g_canal_2[:]
canal_3 = g_canal_3[:]
chan_no = self.chan_var.get()
if (len(canal_1) != 0): # Apply only if data available
self.plot(self.tab1, self.tab2, self.tab3, self.tab4, canal_1, canal_2, canal_3, chan_no)
def open_file(self):
"""Opens dialog to select a file, reads data from file and plots the data"""
ftypes = [('Text files', '*.txt'), ('All files', '*')]
dlg = filedialog.Open(root, filetypes=ftypes)
fl = dlg.show()
if fl != '':
# Open file for reading
arch = open(fl, "r")
datos_arch = arch.read()
# Searches for every channel, delimited by L1, L2 and L3 tags.
canal_1 = extraer_int_tag(datos_arch, 'L1')
canal_2 = extraer_int_tag(datos_arch, 'L2')
canal_3 = extraer_int_tag(datos_arch, 'L3')
print("Amount of samples in channel 1: %s" % len(canal_1))
print("Amount of samples on channel 2: %s" % len(canal_2))
print("Amount of samples on channel 3: %s" % len(canal_3))
message_string = "Amount of samples \nChannel 1: {0} \n".format(len(canal_1))
message_string += "Channel 2: {0} \n".format(len(canal_2))
message_string += "Channel 3: {0} \n".format(len(canal_3))
self.show_message(self.text_message, message_string)
global g_canal_1, g_canal_2, g_canal_3
# Keep a copy of the original values
g_canal_1 = canal_1[:] # Copy list by value not by reference
g_canal_2 = canal_2[:]
g_canal_3 = canal_3[:]
self.plot(self.tab1, self.tab2, self.tab3, self.tab4, canal_1, canal_2, canal_3, chan_no=1)
if __name__ == '__main__':
root = Tk.Tk()
root.title('FFT spectrum analyser')
app = Application(root)
root.mainloop()
