aboutsummaryrefslogtreecommitdiff
path: root/eitControl.py
diff options
context:
space:
mode:
Diffstat (limited to 'eitControl.py')
-rw-r--r--eitControl.py422
1 files changed, 0 insertions, 422 deletions
diff --git a/eitControl.py b/eitControl.py
deleted file mode 100644
index 10832fc..0000000
--- a/eitControl.py
+++ /dev/null
@@ -1,422 +0,0 @@
-from pyqtgraph.Qt import QtGui, QtCore
-import pyqtgraph as pg
-from pyqtgraph.dockarea import *
-import pyqtgraph.exporters
-
-import platform
-import os
-import argparse
-import ast
-
-from threading import Thread
-
-import time # For sleep, clock, time and perf_counter
-from datetime import datetime, timedelta, date
-
-import numpy as np
-import csv
-
-import ue9qol
-from funcGenerator import Sweeper, SinGen, TriangleGen, RampGen, PulseGen, SawGen
-import rfGen
-
-from configparser import ConfigParser, ExtendedInterpolation
-config = ConfigParser(interpolation=ExtendedInterpolation(),
- converters = {'list': lambda x: [i.strip() for i in x.strip().split('\n')]})
-default_config = """
-[RF]
-central_frequency = 6.83468e9
-frequency_span = 100e3
-initial_frequency = 6.834e9
-frequency_export_name = rfFreq
-
-[DAQ]
-# channels to grab and their meaning
-ain0 = transmission
-ain1 = lockin
-ain2 = davll
-ain3 = ain3_undefined
-# commented out channels will not be processed or stored
-# dac0 = dac0_undefined
-# dac1 = dac1_undefined2
-
-[Plot]
-x_axis_data = rfFreq
-
-[Plot_channels_visibility]
-transmission = yes
-lockin = yes
-davll = yes
-
-[Plot_channels_colors]
-# color specification will be evaluated and should match PyQtGraph.mkColor(args)
-# for example
-# (R,G,B,Alpha) tuple: example_trace = (255,0,255,100)
-# or color name: example_trace = 'g'
-transmission = (20,20,20,100)
-lockin = (85,170,255,100)
-davll = (255,0,255,100)
-ain3_undefined = (0,85,255,100)
-
-
-[Save]
-save_prefix = eit
-data_dir = z:\data.VAMPIRE
-
-"""
-
-config.read_string(default_config)
-# additional config
-config.read("config.ini")
-
-
-class Experiment:
-
- def __init__(self, root, config, args):
- self.root = root
- self.config = config
- self.save_cnt = 0
- self.tic = 0
- self.newDataIsReady = False
- self.xlabel=''
- self.channelGraph={}
- self.clearData()
- self.hardware = {}
- self.hardwareSetup(args)
-
- # now we ready to do gui
- self.buttons = {}
- self.guiSweeper = Sweeper(self.root, Npoints=10, SweepTime=1, onTicCallbacks=[self.updatePlot])
- self.guiSetup(root)
- self.guiSweeper.cmdStart()
-
-
- def hardwareSetup(self,args):
- if args.test:
- print("Test mode, run with fake hardware")
- self.sweeper = Sweeper(self.root, Npoints=100, SweepTime=1, onTicCallbacks=[self.onTic])
- self.hardware['LabJack'] = ue9qol.UE9qolDummy(sweeper=self.sweeper)
- self.hardware['rfGen'] = rfGen.rfGenLMX2487Dummy(port='/dev/ttyUSB0', speed=115200, timeout=1)
- else:
- self.sweeper = Sweeper(self.root, Npoints=500, SweepTime=10, onTicCallbacks=[self.onTic])
- self.hardware['LabJack'] = ue9qol.UE9qol()
- if platform.system() == 'Linux':
- rf=rfGen.rfGenLMX2487(port='/dev/ttyUSB0', speed=115200, timeout=1)
- else:
- rf=rfGen.rfGenLMX2487(port='COM5', speed=115200, timeout=1)
- self.hardware['rfGen'] = rf
-
- fCent = self.config['RF'].getfloat('central_frequency', fallback=6.83468e9)
- fSpan = self.config['RF'].getfloat('frequency_span', fallback=100e3)
- rf_f_init = self.config['RF'].getfloat('initial_frequency', fallback=6.834e9)
- self.hardware['rfGen'].setFreq(rf_f_init)
- self.rfGenFunc = SawGen(start=0, stop=0, sweeper = self.sweeper, duty_cycle=0.9)
- self.rfGenFunc.setCenter(fCent)
- self.rfGenFunc.setSpan(fSpan)
-
- self.funcGen = TriangleGen(start=0, stop=5, sweeper = self.sweeper)
-
-
- def centralFreqValueChanged(self, sb):
- v=sb.value()
- self.config['RF']['central_frequency'] = str(v)
- self.rfGenFunc.setCenter(v)
- pass
-
- def freqSpanValueChanged(self, sb):
- v=sb.value()
- self.config['RF']['frequency_span'] = str(v)
- self.rfGenFunc.setSpan(v)
- pass
-
- def guiSetup(self, root):
- self.dockArea = area = DockArea()
- d1 = Dock("Global", size=(5,1))
- d2 = Dock("Data", size=(100,100))
- d3 = Dock("RF Gen", size=(1,2))
- dS = Dock("Status", size=(1,2), autoOrientation=False)
- dS.setOrientation(o='horizontal')
- area.addDock(d1, 'top')
- area.addDock(dS, 'bottom', d1)
- area.addDock(d2, 'bottom', dS)
- area.addDock(d3, 'bottom', d2)
- self.root.addWidget(area)
-
- self.dataPlot = pg.PlotWidget(name='Plot1')
- d2.addWidget(self.dataPlot)
- self.dataPlot.showGrid(x=True, y=True)
- self.dataPlot.addLegend()
- self.vLineSweepPosition = vLine = pg.InfiniteLine(angle=90, movable=False, pen='r')
- self.dataPlot.addItem(vLine, ignoreBounds=True)
-
- # global buttons
- w1 = pg.LayoutWidget()
- bAutoZoom = QtGui.QPushButton('&AutoZoom')
- bAutoZoom.clicked.connect(self.autoZoom)
- self.buttons["AutoZoom"] = bAutoZoom
- bRestart = QtGui.QPushButton('&Restart')
- bRestart.clicked.connect(self.restart)
- self.buttons["Restart"] = bRestart
- bStartStopToggle = QtGui.QPushButton('&Start')
- bStartStopToggle.clicked.connect(self.start)
- self.buttons["StartStopToggle"] = bStartStopToggle
- bSave = QtGui.QPushButton('Sa&ve data')
- bSave.clicked.connect(self.saveCmd)
- self.buttons["Save"] = bSave
- bExit = QtGui.QPushButton('&Exit')
- bExit.clicked.connect(exit)
- self.buttons["Exit"] = bExit
- bSaveConfig = QtGui.QPushButton('Save Con&fig')
- bSaveConfig.clicked.connect(self.saveConfigCmd)
- self.buttons["SaveConfig"] = bSaveConfig
- w1.addWidget(bAutoZoom, row=0, col=0)
- w1.addWidget(bRestart, row=0, col=1)
- w1.addWidget(bStartStopToggle, row=0, col=2)
- w1.addWidget(bSave, row=0, col=3)
- w1.addWidget(bSaveConfig, row=0, col=4)
- w1.addWidget(bExit, row=0, col=5)
- d1.addWidget(w1)
-
- ## status line
- self.statusline = l = QtGui.QLabel("All ok")
- dS.addWidget(l, row=1, col=0)
-
- # RF gen gui
- fCent=self.rfGenFunc.getCenter()
- fSpan=self.rfGenFunc.getSpan()
- spins = [
- ("Central Frequency",
- pg.SpinBox(value=fCent, bounds=[6.83e9, 6.84e9], suffix='Hz', siPrefix=True, step=1e3, decimals=10),
- self.centralFreqValueChanged),
- ("Frequency Span",
- pg.SpinBox(value=fSpan, bounds=[1, 10e6], dec=True, step=0.5, suffix='Hz', siPrefix=True, minStep=1),
- self.freqSpanValueChanged)
- ]
- w3 = pg.LayoutWidget()
- d3.addWidget(w3)
- for text, spin, cb in spins:
- l=QtGui.QLabel(text)
- w3.addWidget(l)
- w3.addWidget(spin)
- spin.sigValueChanged.connect(cb)
-
- def clearData(self):
- self.data = {}
- # RF generator channels
- rf_freq_Name = self.config['RF'].get('frequency_export_name')
- self.data[rf_freq_Name] = []
- # DAQ channels
- for ch in self.config['DAQ']:
- ch_meaning = self.config['DAQ'][ch]
- self.data[ch_meaning] = []
- # special channels
- self.data['tic'] = []
- self.data['x'] = []
-
-
- def stop(self):
- self.sweeper.cmdStop()
- self.buttons["StartStopToggle"].setText("&Continue")
- self.buttons["StartStopToggle"].clicked.disconnect()
- self.buttons["StartStopToggle"].clicked.connect(self.start)
-
- def start(self):
- self.sweeper.cmdStart()
- self.buttons["StartStopToggle"].setText("&Pause")
- self.buttons["StartStopToggle"].clicked.disconnect()
- self.buttons["StartStopToggle"].clicked.connect(self.stop)
-
- def restart(self):
- self.clearData()
- self.sweeper.cmdRestart()
- self.buttons["StartStopToggle"].setText("&Pause")
- self.buttons["StartStopToggle"].clicked.disconnect()
- self.buttons["StartStopToggle"].clicked.connect(self.stop)
-
- def getNewDataFileName(self, ext="csv"):
- data_dir = self.config['Save'].get('data_dir', fallback='unset_data_dir')
- if not os.path.exists(data_dir):
- os.mkdir(data_dir)
- if not os.path.isdir(data_dir):
- print(f"ERROR: cannot create directory for data: {data_dir}")
- print(f"Will use current dir for storage")
- data_dir = "."
-
- prefix = self.config['Save'].get('save_prefix', fallback='unset_experiment_prefix')
- today = date.today()
- datestr = today.strftime("%Y%m%d")
- self.save_cnt += 1
-
- base_name = f"{prefix}_{datestr}_{self.save_cnt:#04}"
- file_name = f"{base_name}.{ext}"
- data_file = os.path.join(data_dir, file_name)
- if os.path.exists(data_file):
- data_file = self.getNewDataFileName(ext=ext)
- return data_file
-
- def saveCmd(self):
- csv_file = self.getNewDataFileName(ext='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')
- msg = f"data saved to {csv_file}"
- print(msg)
- self.statusline.setText(msg)
-
- png_file=csv_file.replace(".csv", ".png")
- print(f"Picture saved to {png_file}")
- plt = self.dataPlot.getPlotItem()
- ex = pg.exporters.ImageExporter(plt)
- if pg.__version__ == '0.10.0':
- # Workaround for PyQtGraph version <= 0.10.0
- # see https://github.com/pyqtgraph/pyqtgraph/issues/538#issuecomment-361405356
- w= int(plt.width())
- h= int(plt.height())
- # the value in setValue need to be different from default
- # otherwise it will not be taken
- ex.parameters().param('width').setValue(w+1, blockSignal=ex.widthChanged)
- ex.parameters().param('height').setValue(h+1, blockSignal=ex.heightChanged)
- # now we set actual value
- # ex.parameters()['width'] = w
- ex.parameters().param('width').setValue(w, blockSignal=ex.widthChanged)
- ex.parameters().param('height').setValue(h, blockSignal=ex.heightChanged)
- # beware this is bad workaround!!! plot data is misplaced
- ex.export(png_file)
-
- def saveConfigCmd(self):
- with open('config.ini', 'w') as configfile:
- self.config.write(configfile)
-
- def onTic(self,swp=None):
- start = datetime.now()
- if swp is None:
- swp = self.sweeper
-
- # RF generator
- rfFreq = self.rfGenFunc.getValue(swp)
- self.hardware['rfGen'].setFreq(rfFreq)
- # skip data update based on independent variable (rfGen) sweep direction
- sweep_direction = self.rfGenFunc.getSweepDirection(swp)
- if sweep_direction < .5:
- # sweep_direction is either 0 or 1
- # we do no data collection on the backward stroke
- return
- rf_freq_Name = self.config['RF'].get('frequency_export_name')
- self.data[rf_freq_Name].append(rfFreq)
-
- # global tic counter
- tic = self.sweeper.getCnt()
- self.data['tic'].append(tic)
-
- # DAQ
- daq0 = self.hardware['LabJack']
-
- # dac0
- # dac0 = self.funcGen.getValue(swp)
- # dac0 = 0
- # dac0 = self.funcGen.getValue(swp)
- # daq0.setOutputCh(0, dac0)
- # self.data['dac0'].append(dac0)
-
- # dac1
- # dac1 = PulseGen(ampl=5, sweeper=swp).getValue()
- # dac1 = 0
- # daq0.setOutputCh(1, dac1)
- # self.data['dac1'].append(dac1)
-
- for ch in self.config['DAQ']:
- if ch[0:3] == 'ain':
- n=int(ch[3:])
- vIn = daq0.getInputCh(n)
- ch_meaning = self.config['DAQ'][ch]
- self.data[ch_meaning].append( vIn )
-
- # X-axis (i.e. independent variable)
- x=self.data[self.config['Plot']['x_axis_data']]
- x=np.array(x)
- fCent = self.rfGenFunc.getCenter()
- x=(x-fCent)
- self.data['x'] = x
- self.dataPlot.setLabel('bottom', 'Frequency offset', units='Hz')
- self.dataPlot.setLabel('left', 'Signal', units='V')
-
- self.newDataIsReady = True
- stop = datetime.now()
- runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000
- # print("onTic DAQ took %s seconds." % (runTime) )
-
- def autoZoom(self):
- self.dataPlot.autoRange()
-
- def updatePlot(self,swp=None):
- if self.newDataIsReady:
- self.newDataIsReady = False
- else:
- return
-
- start = datetime.now()
- x = self.data['x']
- for name in self.config['Plot_channels_visibility']:
- if not self.config['Plot_channels_visibility'].getboolean(name):
- continue
- if name not in self.data:
- continue
- y = self.data[name]
- if name not in self.channelGraph:
- if name in self.config['Plot_channels_colors']:
- color_str = self.config['Plot_channels_colors'][name]
- color = ast.literal_eval(color_str)
- else:
- color = (255,0,0)
- self.channelGraph[name]=self.dataPlot.plot(x,y, pen=None, symbol='o', symbolPen=None, symbolBrush=color, symbolSize=5, name=name)
- else:
- self.channelGraph[name].setData(x,y)
- if len(x)>0:
- self.vLineSweepPosition.setValue(x[-1])
- # centralFreqFormatted = pg.siFormat(self.fCent, suffix='Hz', precision=4)
- # showing trailing zeros is tricky
- fCent = self.rfGenFunc.getCenter()
- centralFreqFormatted = f"{fCent/1e9:.9f}"
- centralFreqFormatted = str.ljust(centralFreqFormatted, 11, '0') + " GHz"
- self.dataPlot.setTitle(f"Signals around center frequency {centralFreqFormatted}")
-
- stop = datetime.now()
- runTime = (stop-start).seconds + float((stop-start).microseconds)/1000000
- print("Replot took %s seconds to plot %s points per channel." % (runTime, len(self.data['x'])) )
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(description='Perform EIT based experiment.')
- parser.add_argument('--test', '-t', action='store_true',
- help='test mode, use fake/dummy hardware')
- parser.add_argument('--config-file', '-c', action='append',
- help='additional config files, could be used multiple time')
- args = parser.parse_args()
-
- if args.config_file:
- for cf in args.config_file:
- print("Reading config file: " + cf)
- config.read(cf)
-
- app = QtGui.QApplication([])
- pg.setConfigOption('background', 'w')
- pg.setConfigOption('foreground', 'k')
- mw = QtGui.QMainWindow()
- mw.setWindowTitle('pyqtgraph example: PlotWidget')
- mw.resize(800,800)
- cw = QtGui.QWidget()
- mw.setCentralWidget(cw)
- l = QtGui.QVBoxLayout()
- cw.setLayout(l)
-
- mw.show()
-
- experiment=Experiment(l, config, args)
- app.exec()
-
-