0

I'm facing a problem working with multiple threads in Python. Let me give the context, I'm trying to read values from an oscilloscope and make a histogram in real time. But the problem is the plotting in real time make my measurements very slow.

So I thought to read the values from the oscilloscope in one thread and then plot the histogram in another thread - which should fix the slowness. Since I'm a beginner with python and I'm getting a lot of errors.

This is my code I wrote:

    import time
    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.ticker import PercentFormatter
    from numpy.core.shape_base import block
    import pyvisa
    import os
    from matplotlib.pyplot import  figure, step
    from scipy.signal import find_peaks
    import queue
    import threading

    def acquisition(queue, name):
        rm = pyvisa.ResourceManager()
        rta = rm.open_resource('TCPIP::192.168.100.101::INSTR')

        path='Desktop/Laboratorio/Programacion- 
        Automatizacion/Pyvisa/Output/'

        rta.write("MEASurement1:TIMeout:AUTO")

        valuep = []
        aux = 0

        #Comprobamos que no existen ya esos ficheros
        if os.path.isfile(path + name + '.txt'):
            os.remove(path + name + '.txt') 

        if os.path.isfile(path + name + '.png'):
            os.remove(path + name + '.png') 

        start_time = time.time()

        aux
        entries=1000
        gain=0  
        completed = 0

        while len(valuep) < entries:

            if rta.query("*OPC?"):
                p1=float(rta.query("CURSor1:Y1Position?"))
            if rta.query("*OPC?"):
                p2=float(rta.query("CURSor1:Y2Position?"))

            r=(p1-p2)
            valuep.append(r)
            a = np.array(valuep)

            completed = len(valuep)/entries*100
            print(str(completed) + ' %\n')

            hist, bin_edges = np.histogram(a, 300)
            bin_edges = bin_edges[1:]
            peaks, _ = find_peaks(hist, distance=10, prominence=10)
            
            sumDelta = 0

            if len(peaks) >= 2:
                sumDelta = 0
                for i in range(len(peaks)-1):
                    deltaV=bin_edges[peaks[i+1]]-bin_edges[peaks[i]]
                    sumDelta += deltaV
                    gain=(sumDelta/(len(peaks)-1))/(50*1.602e-19)

            if completed == 10 or completed == 20 or completed == 30 or 
            completed == 40 or completed == 50 or completed == 60 or 
            completed == 70 or completed == 80 or completed == 90 or 
            completed == 100:
                auxLen = np.arange(0 , len(valuep) , 1)
                with open(path + name + '.txt', 'w') as f:
                    f.write(str(gain) + ' | ' + str(round((time.time() - 
                    start_time)/60, 3)) + '\n')
                    for i in auxLen:
                        f.write(str(valuep[i]))
                        f.write('\n')
            message = a
            queue.put(message)

        rta.close()

    def histogram(queue, name):
        while True:
            message = queue.get()
            plt.cla()
            # plt.xlabel("Vs | %=" + str(round(len(valuep)/entries*100, 3)) + " | Time: " + str(round((time.time() - start_time)/60, 3)) + " min")
            plt.ylabel("Bins")
            plt.yscale('log')
            hist, bin_edges = np.histogram(message, 300)
            bin_edges = bin_edges[1:]
            plt.plot(bin_edges, hist)
            # plt.plot(bin_edges[peaks], hist[peaks], "x")
            plt.show()
            plt.tight_layout()
    
    
    if __name__ == "__main__":
        print('Input code name: ')
        name = input()
        q = queue.Queue()
        acquire = threading.Thread(target=acquisition, args=(q, name,))
        hist = threading.Thread(target=histogram, args=(q, name,))
        acquire.start()
        hist.start()
        acquire.join()
        hist.join()
        q.join()
        plt.plot()

The function acquire reads values from oscilloscope and return it to a queue as a numpy array.

The error is:

UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.

I really appreciate your help and any comments, to how should I proceed. Thanks in advance.

This is the code I had at first:

#librerias
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import PercentFormatter
import pyvisa
import time
import os
from itertools import count
from matplotlib.animation import FuncAnimation
from matplotlib.pyplot import  figure, step
from scipy.signal import find_peaks

#Abrir sesion VISA
rm = pyvisa.ResourceManager()
rta = rm.open_resource('TCPIP::192.168.100.101::INSTR')

print('Input code name: ')
name=input()

path='Desktop/Laboratorio/Programacion-Automatizacion/Pyvisa/Output/'

rta.write("MEASurement1:TIMeout:AUTO")
rta.write("SYSTem:COMMunicate:INTerface:ETHernet:TRANsfer FD100")
rta.write("FORM BIN") # // Set BIN data format

valuep = []
aux = 0

#Comprobamos que no existen ya esos ficheros
if os.path.isfile(path + name + '.txt'):
    os.remove(path + name + '.txt') 

if os.path.isfile(path + name + '.png'):
    os.remove(path + name + '.png') 

start_time = time.time()

def animate(i):
    global aux
    entries=1000
    gain=0  

    if rta.query("*OPC?"):
        p1=float(rta.query("CURSor1:Y1Position?"))
    if rta.query("*OPC?"):
        p2=float(rta.query("CURSor1:Y2Position?"))

    r=(p1-p2)
    valuep.append(r)
    a = np.array(valuep)

    plt.cla()
    plt.xlabel("Vs | %=" + str(round(len(valuep)/entries*100, 3)) + " | Time: " + str(round((time.time() - start_time)/60, 3)) + " min")
    plt.ylabel("Bins")
    plt.yscale('log')

    hist, bin_edges = np.histogram(a, 300)
    bin_edges = bin_edges[1:]
    plt.plot(bin_edges, hist)
    peaks, _ = find_peaks(hist, distance=10, prominence=10)
    
    sumDelta = 0

    if len(peaks) >= 2:
        sumDelta = 0
        for i in range(len(peaks)-1):
            deltaV=bin_edges[peaks[i+1]]-bin_edges[peaks[i]]
            sumDelta += deltaV
        gain=(sumDelta/(len(peaks)-1))/(50*1.602e-19)
        #print(gain)

    plt.plot(bin_edges[peaks], hist[peaks], "x")
    plt.show()

    plt.tight_layout() 

    #Escritura fichero CSV
    if len(valuep) == entries:
        auxLen = np.arange(0 , len(valuep) , 1)
        with open(path + name + '.txt', 'w') as f:
            f.write(str(gain) +'\n')
            for i in auxLen:
                f.write(str(valuep[i]))
                f.write('\n')
        plt.savefig(path + name + '.png')

ani = FuncAnimation(plt.gcf(), animate, interval=0.013)

plt.tight_layout()

plt.show()
rta.close()

This code work just like I want, but very very slow, Im speaking that plotting with aniFunction need 96min for 15000 acquisition and without anifunc need 26min for 30000 acquisition, thats why Im trying with threads.

1 Answers1

0

OK, I have fix it. I have use:

warnings.filterwarnings("ignore")

for warnings and:

ani = FuncAnimation(plt.gcf(), animate, interval=0.013)

to make the animation like before. Now I have a problem with check is queue is empty, if I use:

if not queue.empty():

Dont work, and if I dont use it the program stop bad. I can post the code if u want. Thanks to all.