diff options
author | Eugeniy E. Mikhailov <evgmik@gmail.com> | 2021-04-08 23:47:21 -0400 |
---|---|---|
committer | Eugeniy E. Mikhailov <evgmik@gmail.com> | 2021-04-08 23:47:21 -0400 |
commit | 1fbf0bb826f88f63e84010c629b2f768f8d5e6cd (patch) | |
tree | eb4b19bcc57c30265e0730626162c0fd5899227f /eitControl.py | |
parent | 9c09d346a0120241e1a99537b0b58768f95d1843 (diff) | |
download | qolab-1fbf0bb826f88f63e84010c629b2f768f8d5e6cd.tar.gz qolab-1fbf0bb826f88f63e84010c629b2f768f8d5e6cd.zip |
renamed file
Diffstat (limited to 'eitControl.py')
-rw-r--r-- | eitControl.py | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/eitControl.py b/eitControl.py new file mode 100644 index 0000000..23c611e --- /dev/null +++ b/eitControl.py @@ -0,0 +1,368 @@ +import tkinter as tk +from tkinter import * +from tkinter import ttk + +from threading import Thread + +import time # For sleep, clock, time and perf_counter +from datetime import datetime, timedelta + +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.figure import Figure +from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk) + +import csv + +import ue9qol + +class SinGen: + def __init__(self, ampl=1, offset=0, phase=0, sweeper=None): + self.ampl = ampl + self.phase = phase + self.sweeper = sweeper + self.offset = offset + + def getValue(self, sweeper=None): + if sweeper is None and self.sweeper is None: + print("Error: generator needs sweeper") + return 0 + if sweeper is None: + sweeper = self.sweeper + return self.ampl * np.sin( 2*np.pi*sweeper.getRelPos()) + self.offset + +class RampGen: + # monotonically goes from start to stop, at final point move to start + def __init__(self, start=0, stop=0, sweeper=None): + self.start = start + self.stop = stop + self.sweeper = sweeper + + def getValue(self, sweeper=None): + if sweeper is None and self.sweeper is None: + print("Error: generator needs sweeper") + return 0 + if sweeper is None: + sweeper = self.sweeper + return self.start + sweeper.getRelPos()*(self.stop - self.start) + +class TriangleGen: + # monotonically goes from start to stop, once reaches stop goes back to start + def __init__(self, start=0, stop=0, sweeper=None): + self.start = start + self.stop = stop + self.sweeper = sweeper + + def getValue(self, sweeper=None): + if sweeper is None and self.sweeper is None: + print("Error: generator needs sweeper") + return 0 + if sweeper is None: + sweeper = self.sweeper + if sweeper.getRelPos() < 0.5: + return self.start + 2*sweeper.getRelPos()*(self.stop - self.start) + return self.start + 2*(1-sweeper.getRelPos())*(self.stop - self.start) + +class PulseGen: + # monotonically goes from start to stop, once reaches stop goes back to start + def __init__(self, ampl=1, sweeper=None): + self.ampl = ampl + self.sweeper = sweeper + + def getValue(self, sweeper=None): + if sweeper is None and self.sweeper is None: + print("Error: generator needs sweeper") + return 0 + if sweeper is None: + sweeper = self.sweeper + if sweeper.getRelPos() < 0.5: + return self.ampl + return 0 + +class Sweeper: + def __init__(self, widget, Npoints, SweepTime, onTicCallbacks=[]): + # walk from start to stop with Npoints + # cnt = 1 corresponds to start + # cnt = Npoints corresponds to stop + # variables like relVar are relative to the start of the period + self.cnt = 0 # onTic will increase it right away + self.widget = widget + self.Npoints = Npoints + self.start = 1 + self.stop = self.Npoints + self.SweepTime = SweepTime + self.onTicCallbacks = onTicCallbacks + self.isOn = False + self.isRestart = True + self.isTicRunning = False + + self.span = self.stop - self.start + self.center = (self.stop + self.start)/2 + self.dPos = self.span/(self.Npoints-1) + self.dT = self.SweepTime/(self.Npoints-1) + self.dTmS = round(self.dT*1000) # dT in milliseconds + + def reset(self): + self.cnt = 0 # onTic will increase it right away + self.isRestart = False + self.startTime = datetime.now() + + def onTic(self): + start = datetime.now() + deadline = start + timedelta(milliseconds=self.dTmS) + + if not self.isOn: + self.isTicRunning = False + return + self.isTicRunning = True + if self.isRestart: + self.reset() + self.isRestart = False + self.incr() + for cb in self.onTicCallbacks: + cb(self) + stop = datetime.now() + self.isTicRunning = False + if stop > deadline: + runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000 + print("Overrun: Callbacks took %s seconds instead of %s" % (runTime, self.dTmS/1000) ) + self.widget.after(0, self.onTic) + + idleTime_mS = round((deadline-stop).seconds * 1000 + (deadline-stop).microseconds/1000) + # print("Will idle for %s" % (idleTime_mS) ) + self.widget.after(idleTime_mS, self.onTic) + + + def cmdRestart(self): + self.cnt = 0 + if self.isOn: + self.isRestart = True + return + self.reset() + self.isOn = True + self.onTic() + + def cmdStart(self): + self.isOn = True + self.onTic() + + def cmdStop(self): + self.isOn = False + + def incr(self): + self.cnt += 1 + self.updPos() + + def updPos(self): + self.relCnt = 1 + ((self.cnt-1) % self.Npoints) + self.pos = self.start + self.dPos * (self.relCnt - 1) + self.relPos = (self.pos-self.start)/self.span + + def getCnt(self): + return self.cnt + + def getRelCnt(self): + return self.relCnt + + def getPos(self): + return self.pos + + def getRelPos(self): + return self.relPos + + +class Experiment: + + def __init__(self, root): + self.root = root + self.tic = 0 + self.channelsNames2plot={'dac0', 'dac1', 'adc1', 'adc2', 'adc3', 'adc4'} + self.xChannelName='dac0' # can be also 'tic' or any of above + self.xlabel='Frequency (Hz)' + self.lines2plot={} + self.clearData() + self.guiSetup(root) + self.guiSweeper = Sweeper(self.root, Npoints=2, SweepTime=1, onTicCallbacks=[self.updatePlot]) + self.guiSweeper.cmdStart() + self.hardware = {} + # self.hardwareSetup() + self.sweeper = Sweeper(self.root, Npoints=100, SweepTime=1, onTicCallbacks=[self.onTic]) + # self.funcGen = SinGen(2, 2, sweeper = self.sweeper) + # self.funcGen = RampGen(0, 5, sweeper = self.sweeper) + self.funcGen = TriangleGen(0, 5, sweeper = self.sweeper) + + + + def hardwareSetup(self): + self.hardware['LabJack'] = ue9qol.UE9qol() + + def guiSetup(self, root): + self.cntrlPannel=ttk.LabelFrame(root, text='controls') + self.cntrlPannel.pack() + + self.bAutoZoom=Button(self.cntrlPannel,text="AutoZoom",command=self.autoZoom,font=('Arial','24')) + self.bAutoZoom.pack(side='left') + + self.bRestart=Button(self.cntrlPannel,text="RESTART",command=self.restart,font=('Arial','24')) + self.bRestart.pack(side='left') + + self.bStart=Button(self.cntrlPannel,text="START",command=self.start,font=('Arial','24')) + self.bStart.pack(side='left') + + self.bStop=Button(self.cntrlPannel,text="STOP",command=self.stop,font=('Arial','24')) + self.bStop.pack(side='left') + + self.bSave=Button(self.cntrlPannel,text="SAVE",command=self.saveCmd,font=('Arial','24')) + self.bSave.pack(side='left') + + self.bExit=Button(self.cntrlPannel,text="EXIT",command=exit,font=('Arial','24')) + self.bExit.pack(side='left') + + self.dataDisplay=ttk.LabelFrame(root, text='data') + self.dataDisplay.pack() + + self.fig=plt.figure(figsize=[32, 24]) + + self.ax = self.fig.add_subplot(1,1,1) + # self.ax.set_xlim([0,20]) + # self.ax.set_ylim([0,20]) + # self.ax.plot([i for i in range(10)],[i for i in range(10)]) + self.line, = self.ax.plot(self.data['tic'], self.data['adc1'], '.') + + self.canvas = FigureCanvasTkAgg(self.fig, master = self.dataDisplay) + self.canvas.draw() + + # placing the canvas on the Tkinter window + # self.canvas.get_tk_widget().pack() + + # creating the Matplotlib toolbar + self.toolbar = NavigationToolbar2Tk(self.canvas, self.dataDisplay) + self.toolbar.update() + + # placing the toolbar on the Tkinter window + self.canvas.get_tk_widget().pack() + + def clearData(self): + self.data = {} + self.data['tic'] = [] + self.data['x'] = [] + self.data['dac0'] = [] + self.data['dac1'] = [] + self.data['adc1'] = [] + self.data['adc2'] = [] + self.data['adc3'] = [] + self.data['adc4'] = [] + + def stop(self): + self.sweeper.cmdStop() + + def start(self): + self.sweeper.cmdStart() + + def restart(self): + self.clearData() + self.sweeper.cmdRestart() + + def saveCmd(self): + csv_file = 'data.csv' + data = self.data + try: + with open(csv_file, 'w') as csvfile: + writer = csv.writer(csvfile) + writer.writerow(data.keys()) + writer.writerows(zip(*data.values())) + except IOError: + print('I/O error') + + def onTic(self,swp=None): + start = datetime.now() + if swp is None: + swp = self.sweeper + + # global tic counter + tic = self.sweeper.getCnt() + self.data['tic'].append(tic) + + # DAQ + # daq0 = self.hardware['LabJack'] + + # dac0 + dac0 = self.funcGen.getValue(swp) + # daq0.setOutputCh(0, out0) + self.data['dac0'].append(dac0) + + # dac1 + dac1 = PulseGen(ampl=5, sweeper=swp).getValue() + # daq0.setOutputCh(0, dac1) + self.data['dac1'].append(dac1) + + # adc1 + # adc1= daq0.getInputCh(1) + adc1 = SinGen(ampl=1, sweeper=swp).getValue() + self.data['adc1'].append( adc1 ) + + # adc2 + # adc2= daq0.getInputCh(2) + adc2 = SinGen(ampl=2, sweeper=swp).getValue() + self.data['adc2'].append( adc2 ) + + # adc3 + # adc3= daq0.getInputCh(3) + adc3 = SinGen(ampl=3, sweeper=swp).getValue() + self.data['adc3'].append( adc3 ) + + # adc4 + # adc4= daq0.getInputCh(4) + adc4 = SinGen(ampl=4, sweeper=swp).getValue() + self.data['adc4'].append( adc4 ) + + # X-axis (i.e. independent variable) + # self.data['x'].append(tic) + self.data['x']=self.data[self.xChannelName] + + stop = datetime.now() + runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000 + # print("onTic DAQ took %s seconds." % (runTime) ) + + def autoZoom(self): + self.ax.cla() + x = self.data['x'] + for name in self.channelsNames2plot: + if name not in self.data: + continue + y = self.data[name] + self.lines2plot[name], = self.ax.plot(x, y, '.', label=name) + self.ax.legend() + plt.xlabel(self.xlabel) + self.canvas.draw() + + def updatePlot(self,swp=None): + start = datetime.now() + # self.ax.cla() + # self.line, = self.ax.plot(self.data['tic'], self.data['adc1'], '.') + # t = Thread(target=self.canvas.draw) + # self.line.set_data([.1, .2, .3], [.1, .2, .3]) + x = self.data['x'] + for name in self.channelsNames2plot: + if name not in self.data: + continue + y = self.data[name] + if name in self.lines2plot: + ln = self.lines2plot[name] + ln.set_data(x, y) + self.ax.draw_artist(ln) + # self.canvas.update() + # self.canvas.draw() + self.fig.canvas.flush_events() + stop = datetime.now() + runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000 + print("Replot took %s seconds to plot %s points." % (runTime, len(self.data['adc1'])) ) + + +if __name__ == '__main__': + root=Tk() + + experiment=Experiment(root) + + root.mainloop() + |