diff --git a/.gitignore b/.gitignore index 35c39401187f8fdd5b7081aa5099e33a7d5c6833..e6133b777200ab3b5af5cebedc528094037d5869 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ Breakpoints_v2.xcbkptlist *.xcuserstate +*.o +*.c +*.so *.pyc *~ /dstatInterface/dist/ diff --git a/dstatInterface/dstatInterface.xcodeproj/project.pbxproj b/dstatInterface/dstatInterface.xcodeproj/project.pbxproj index e64de9bcfcf06db6a4cfb58ece71ee1afa71834f..6371382929b9f627d60a7074a43dc02e99d5e76e 100644 --- a/dstatInterface/dstatInterface.xcodeproj/project.pbxproj +++ b/dstatInterface/dstatInterface.xcodeproj/project.pbxproj @@ -13,12 +13,14 @@ 5FCB54231905B6EE00CEB148 /* dstat_comm.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = dstat_comm.py; sourceTree = ""; }; 5FDC0DFD18FDAD79003F857A /* mpl.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = mpl.py; sourceTree = ""; }; 5FDC1E4218FF9572007AD04D /* glade1.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = glade1.py; sourceTree = ""; }; + 5FF00FDC1942BD16004D38A8 /* setup.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = setup.py; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ 5FDC0DF218FDACDA003F857A = { isa = PBXGroup; children = ( + 5FF00FDC1942BD16004D38A8 /* setup.py */, 5F87883C19072E86007B53E0 /* mpltest.py */, 5FCB541D1905923800CEB148 /* interface_test.py */, 5FCB54231905B6EE00CEB148 /* dstat_comm.py */, @@ -33,7 +35,7 @@ /* Begin PBXLegacyTarget section */ 5FDC0DF718FDACDA003F857A /* dstatInterface */ = { isa = PBXLegacyTarget; - buildArgumentsString = "$(ACTION)"; + buildArgumentsString = "setup.py build_ext --inplace"; buildConfigurationList = 5FDC0DFA18FDACDA003F857A /* Build configuration list for PBXLegacyTarget "dstatInterface" */; buildPhases = ( ); diff --git a/dstatInterface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme b/dstatInterface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme index 238431933e672a423b4880b7e6f0ec23115ec75c..ddd198194ed57a640ffffec8267410bc6925a202 100644 --- a/dstatInterface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme +++ b/dstatInterface/dstatInterface.xcodeproj/xcuserdata/mdryden.xcuserdatad/xcschemes/dstatInterface.xcscheme @@ -41,11 +41,20 @@ debugDocumentVersioning = "YES" allowLocationSimulation = "YES"> + FilePath = "/usr/local/Cellar/python/2.7.6_1/Frameworks/Python.framework/Versions/2.7/bin/python2.7"> + + + + diff --git a/dstatInterface/dstat_comm.py b/dstatInterface/dstat_comm.py index fa374b5493c1a37d725139358a1a93fe2ebef1d0..41476f580cfa0420db134224bd6f08e6b02e1303 100644 --- a/dstatInterface/dstat_comm.py +++ b/dstatInterface/dstat_comm.py @@ -1,9 +1,11 @@ #!/usr/bin/env python -import serial, io, time, struct +import serial, io, time, struct, sys, os from types import * from serial.tools import list_ports import numpy as np +import multiprocessing as mp +from Queue import Empty class delayedSerial(serial.Serial): #overrides normal serial write so that characters are output individually with a slight delay def write(self, data): @@ -11,28 +13,29 @@ class delayedSerial(serial.Serial): #overrides normal serial write so that chara serial.Serial.write(self, i) time.sleep(.001) -class linearData: - # constr - def __init__(self): - self.xdata = [] - self.ydata = [] - self.first = 1 - - # add data - def add(self, data): - if self.first == 1: - self.first = 0 - return - assert(len(data) == 2) - self.x.append(data[0]) - self.y.append(data[1]) - - # clear data - def clear(self): - self.first = 1 - self.ax = [] - self.ay = [] +class dataCapture(mp.Process): + def __init__(self, ser_instance, pipe): + mp.Process.__init__(self) + + self.serial = ser_instance + self.recv_p, self.send_p = pipe + + def run(self): + sys.stdout.write('[%s] running ... process id: %s\n' + % (self.name, os.getpid())) + while True: + for line in self.serial: + if line.startswith('B'): + self.send_p.send(self.serial.read(size=6))#uint16 + int32 + + elif line.lstrip().startswith("no"): + self.serial.flushInput() + self.send_p.close() #causes EOF at other end of pipe + print "closed" + break + + break class SerialDevices: def __init__(self): @@ -50,6 +53,7 @@ class Experiment: pass def init(self): + self.data_extra = [] #must be defined even when not needed self.__gaintable = [1e2, 3e2, 3e3, 3e4, 3e5, 3e6, 3e7, 5e8] self.gain = self.__gaintable[int(self.parameters['gain'])] self.updatelimit = self.view_parameters['updatelimit'] @@ -103,32 +107,30 @@ class Experiment: self.plot.redraw() self.ser.close() - + def data_handler(self): + recv_p, send_p = mp.Pipe(duplex=False) + + capture_proc = dataCapture(self.ser, (recv_p, send_p)) + capture_proc.start() + send_p.close() #pipe won't trip EOFError unless all connections are closed + + updatetime = 0 + while True: - for line in self.ser: - if line.startswith('B'): - inputdata = self.ser.read(size=6) #uint16 + int32 - voltage, current = struct.unpack(' .2): + self.plot.updateline(self, 0) + self.plot.redraw() + updatetime = float(time.time()) - break + except EOFError: + print "empty" + break def data_postprocessing(self): pass @@ -206,6 +208,14 @@ class lsv_exp(Experiment): self.init() #need to call after xmin and xmax are set self.commands += "L" + self.commands[2] += str(self.parameters['clean_s']) + self.commands[2] += " " + self.commands[2] += str(self.parameters['dep_s']) + self.commands[2] += " " + self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768)) + self.commands[2] += " " + self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768)) + self.commands[2] += " " self.commands[2] += str(self.parameters['start']) self.commands[2] += " " self.commands[2] += str(self.parameters['stop']) @@ -221,7 +231,7 @@ class cv_exp(Experiment): self.databuffer = databuffer_instance self.datatype = "CVData" - self.xlabel = "Voltage (DAC units)" + self.xlabel = "Voltage (mV)" self.ylabel = "Current (A)" self.data = [[],[]] #Will have to alter data_handler to add new lists as needed self.datalength = 2 * self.parameters['scans'] #x and y for each scan @@ -231,6 +241,14 @@ class cv_exp(Experiment): self.init() self.commands += "C" + self.commands[2] += str(self.parameters['clean_s']) + self.commands[2] += " " + self.commands[2] += str(self.parameters['dep_s']) + self.commands[2] += " " + self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768)) + self.commands[2] += " " + self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768)) + self.commands[2] += " " self.commands[2] += str(self.parameters['v1']) self.commands[2] += " " self.commands[2] += str(self.parameters['v2']) @@ -291,17 +309,26 @@ class swv_exp(Experiment): self.databuffer = databuffer_instance self.datatype = "SWVData" - self.xlabel = "Voltage (DAC units)" + self.xlabel = "Voltage (mV)" self.ylabel = "Current (A)" - self.data = [[],[],[],[]] #one extra for difference - self.datalength = 4 + self.data = [[],[]] #only difference stored here + self.datalength = 2 * self.parameters['scans'] self.xmin = self.parameters['start'] self.xmax = self.parameters['stop'] self.init() + self.data_extra = [[],[]] #forward/reverse stored here - needs to be after self.init to keep from being redefined self.commands += "S" + self.commands[2] += str(self.parameters['clean_s']) + self.commands[2] += " " + self.commands[2] += str(self.parameters['dep_s']) + self.commands[2] += " " + self.commands[2] += str(int(self.parameters['clean_mV']*(65536./3000)+32768)) + self.commands[2] += " " + self.commands[2] += str(int(self.parameters['dep_mV']*(65536./3000)+32768)) + self.commands[2] += " " self.commands[2] += str(self.parameters['start']) self.commands[2] += " " self.commands[2] += str(self.parameters['stop']) @@ -312,20 +339,24 @@ class swv_exp(Experiment): self.commands[2] += " " self.commands[2] += str(self.parameters['freq']) self.commands[2] += " " + self.commands[2] += str(self.parameters['scans']) + self.commands[2] += " " def data_handler(self): + scan = 0 + while True: for line in self.ser: if line.startswith('B'): inputdata = self.ser.read(size=10) #uint16 + 2*int32 voltage, forward, reverse = struct.unpack(' True False - Converted Data + Extra Data 2 diff --git a/dstatInterface/interface/dstatinterface.glade b/dstatInterface/interface/dstatinterface.glade index 8c0a51bd271eb613543d980c7b8ea800f3d33338..c476d8fc2939556f9d74018fc37d968c12e3b495 100644 --- a/dstatInterface/interface/dstatinterface.glade +++ b/dstatInterface/interface/dstatinterface.glade @@ -569,7 +569,7 @@ True False - Converted Data + Extra Data 2 diff --git a/dstatInterface/interface/lsv.py b/dstatInterface/interface/lsv.py index e1941f393aaa65bec6c4a8a35f0b9cbd10a46b28..990ca640c8c4471d4bae47205d79302bb72c7000 100644 --- a/dstatInterface/interface/lsv.py +++ b/dstatInterface/interface/lsv.py @@ -7,7 +7,12 @@ class lsv: self.builder = gtk.Builder() self.builder.add_from_file('interface/lsv.glade') self.builder.connect_signals(self) - + + + self.clean_mV = self.builder.get_object('clean_mV') + self.clean_s = self.builder.get_object('clean_s') + self.dep_mV = self.builder.get_object('dep_mV') + self.dep_s = self.builder.get_object('dep_s') self.start_entry = self.builder.get_object('start_entry') self.stop_entry = self.builder.get_object('stop_entry') self.slope_entry = self.builder.get_object('slope_entry') \ No newline at end of file diff --git a/dstatInterface/interface/swv.glade b/dstatInterface/interface/swv.glade index 875db5e3770191b7cd16b0145dde04bb09429aed..2f89f66d5cd4a35c2e85d26e0d67b7534c7d4111 100644 --- a/dstatInterface/interface/swv.glade +++ b/dstatInterface/interface/swv.glade @@ -224,7 +224,7 @@ True False - 5 + 7 2 10 True @@ -404,6 +404,70 @@ 3 + + + True + False + Scan both forwards and backwards. + Cyclic Mode + + + 5 + 6 + + + + + True + False + Scans + + + 6 + 7 + + + + + True + True + False + True + True + + + 1 + 2 + 5 + 6 + + + + + + + True + True + + 8 + 0 + 1 + True + False + False + True + True + + + 1 + 2 + 6 + 7 + + GTK_FILL + 3 + + diff --git a/dstatInterface/interface/swv.py b/dstatInterface/interface/swv.py index e4201bdef056c8d67420f369d82061f4724b0e78..a78c44b9ec1cd518c23a5b9c14417a68d492daec 100644 --- a/dstatInterface/interface/swv.py +++ b/dstatInterface/interface/swv.py @@ -8,8 +8,14 @@ class swv: self.builder.add_from_file('interface/swv.glade') self.builder.connect_signals(self) + self.clean_mV = self.builder.get_object('clean_mV') + self.clean_s = self.builder.get_object('clean_s') + self.dep_mV = self.builder.get_object('dep_mV') + self.dep_s = self.builder.get_object('dep_s') self.start_entry = self.builder.get_object('start_entry') self.stop_entry = self.builder.get_object('stop_entry') self.step_entry = self.builder.get_object('step_entry') self.pulse_entry = self.builder.get_object('pulse_entry') - self.freq_entry = self.builder.get_object('freq_entry') \ No newline at end of file + self.freq_entry = self.builder.get_object('freq_entry') + self.cyclic_checkbutton = self.builder.get_object('cyclic_checkbutton') + self.scans_entry = self.builder.get_object('scans_entry') \ No newline at end of file diff --git a/dstatInterface/interface_test.py b/dstatInterface/interface_test.py index 08d2d817f7675f250d9076ca11e5590166fd2498..e16ed93081d1f07982b50faaf5d52e2f838a4679 100644 --- a/dstatInterface/interface_test.py +++ b/dstatInterface/interface_test.py @@ -8,10 +8,17 @@ except: pass try: import gtk + import gobject except: print('GTK not available') sys.exit(1) +try: + import gobject +except: + print('gobject not available') + sys.exit(1) + import interface.adc_pot as adc_pot import interface.chronoamp as chronoamp import interface.lsv as lsv @@ -22,6 +29,7 @@ import interface.pd as pd import interface.save as save import dstat_comm as comm from serial import SerialException +import multiprocessing import mpltest @@ -204,12 +212,25 @@ class main: self.current_exp = comm.chronoamp(parameters, view_parameters, self.plot, self.rawbuffer) self.current_exp.run(self.serial_liststore.get_value(self.serial_combobox.get_active_iter(), 0)) + elif selection == 1: #LSV + parameters['clean_mV'] = int(self.lsv.clean_mV.get_text()) + parameters['clean_s'] = int(self.lsv.clean_s.get_text()) + parameters['dep_mV'] = int(self.lsv.dep_mV.get_text()) + parameters['dep_s'] = int(self.lsv.dep_s.get_text()) parameters['start'] = int(self.lsv.start_entry.get_text()) parameters['stop'] = int(self.lsv.stop_entry.get_text()) parameters['slope'] = int(self.lsv.slope_entry.get_text()) #check parameters are within hardware limits + if (parameters['clean_mV'] > 1499 or parameters['clean_mV'] < -1500): + raise InputError(parameters['clean_mV'],"Clean potential exceeds hardware limits.") + if (parameters['dep_mV'] > 1499 or parameters['dep_mV'] < -1500): + raise InputError(parameters['dep_mV'],"Deposition potential exceeds hardware limits.") + if (parameters['clean_s'] < 0): + raise InputError(parameters['clean_s'],"Clean time cannot be negative.") + if (parameters['dep_s'] < 0): + raise InputError(parameters['dep_s'],"Deposition time cannot be negative.") if (parameters['start'] > 1499 or parameters['start'] < -1500): raise InputError(parameters['start'],"Start parameter exceeds hardware limits.") if (parameters['stop'] > 1499 or parameters['stop'] < -1500): @@ -223,6 +244,10 @@ class main: self.current_exp.run(self.serial_liststore.get_value(self.serial_combobox.get_active_iter(), 0)) elif selection == 2: #CV + parameters['clean_mV'] = int(self.cv.clean_mV.get_text()) + parameters['clean_s'] = int(self.cv.clean_s.get_text()) + parameters['dep_mV'] = int(self.cv.dep_mV.get_text()) + parameters['dep_s'] = int(self.cv.dep_s.get_text()) parameters['start'] = int(self.cv.start_entry.get_text()) parameters['slope'] = int(self.cv.slope_entry.get_text()) parameters['v1'] = int(self.cv.v1_entry.get_text()) @@ -230,6 +255,14 @@ class main: parameters['scans'] = int(self.cv.scans_entry.get_text()) #check parameters are within hardware limits + if (parameters['clean_mV'] > 1499 or parameters['clean_mV'] < -1500): + raise InputError(parameters['clean_mV'],"Clean potential exceeds hardware limits.") + if (parameters['dep_mV'] > 1499 or parameters['dep_mV'] < -1500): + raise InputError(parameters['dep_mV'],"Deposition potential exceeds hardware limits.") + if (parameters['clean_s'] < 0): + raise InputError(parameters['clean_s'],"Clean time cannot be negative.") + if (parameters['dep_s'] < 0): + raise InputError(parameters['dep_s'],"Deposition time cannot be negative.") if (parameters['start'] > 1499 or parameters['start'] < -1500): raise InputError(parameters['start'],"Start parameter exceeds hardware limits.") if (parameters['slope'] > 2000 or parameters['slope'] < 1): @@ -247,13 +280,32 @@ class main: self.current_exp.run(self.serial_liststore.get_value(self.serial_combobox.get_active_iter(), 0)) elif selection == 3: #SWV + parameters['clean_mV'] = int(self.swv.clean_mV.get_text()) + parameters['clean_s'] = int(self.swv.clean_s.get_text()) + parameters['dep_mV'] = int(self.swv.dep_mV.get_text()) + parameters['dep_s'] = int(self.swv.dep_s.get_text()) parameters['start'] = int(self.swv.start_entry.get_text()) parameters['stop'] = int(self.swv.stop_entry.get_text()) parameters['step'] = int(self.swv.step_entry.get_text()) parameters['pulse'] = int(self.swv.pulse_entry.get_text()) parameters['freq'] = int(self.swv.freq_entry.get_text()) + if self.swv.cyclic_checkbutton.get_active(): + parameters['scans'] = int(self.swv.scans_entry.get_text()) + if parameters['scans'] < 1: + raise InputError(parameters['scans'],"Must have at least one scan.") + else: + parameters['scans'] = 0 + #check parameters are within hardware limits (doesn't check if pulse will go out of bounds, but instrument checks this (I think)) + if (parameters['clean_mV'] > 1499 or parameters['clean_mV'] < -1500): + raise InputError(parameters['clean_mV'],"Clean potential exceeds hardware limits.") + if (parameters['dep_mV'] > 1499 or parameters['dep_mV'] < -1500): + raise InputError(parameters['dep_mV'],"Deposition potential exceeds hardware limits.") + if (parameters['clean_s'] < 0): + raise InputError(parameters['clean_s'],"Clean time cannot be negative.") + if (parameters['dep_s'] < 0): + raise InputError(parameters['dep_s'],"Deposition time cannot be negative.") if (parameters['start'] > 1499 or parameters['start'] < -1500): raise InputError(parameters['start'],"Start parameter exceeds hardware limits.") if (parameters['step'] > 200 or parameters['step'] < 1): @@ -291,11 +343,22 @@ class main: self.databuffer.set_text("") self.databuffer.place_cursor(self.databuffer.get_start_iter()) + self.rawbuffer.set_text("") + self.rawbuffer.place_cursor(self.rawbuffer.get_start_iter()) + + for col in zip(*self.current_exp.data): + for row in col: + self.rawbuffer.insert_at_cursor(str(row)+ "\t") + self.rawbuffer.insert_at_cursor("\n") - for i in zip(*self.current_exp.data): - for j in i: - self.databuffer.insert_at_cursor(str(j)+ "\t") - self.databuffer.insert_at_cursor("\n") + + if self.current_exp.data_extra: + for col in zip(*self.current_exp.data_extra): + for row in col: + self.databuffer.insert_at_cursor(str(row)+ "\t") + self.databuffer.insert_at_cursor("\n") + + self.spinner.stop() def on_file_save_exp_activate(self, menuitem, data=None): @@ -304,5 +367,7 @@ class main: if __name__ == "__main__": + multiprocessing.freeze_support() + gobject.threads_init() main = main() gtk.main() \ No newline at end of file diff --git a/dstatInterface/mpltest.py b/dstatInterface/mpltest.py index e72c6fc4c07c2e8a70fc9c1720b666d1f951374e..516d8032dc57a5c5397d4444a0879752d21bd26e 100644 --- a/dstatInterface/mpltest.py +++ b/dstatInterface/mpltest.py @@ -45,8 +45,10 @@ class plotbox: self.lines.append(self.axe1.plot([0,1], [0,1])[0]) def updateline(self, Experiment, line_number): - self.lines[line_number].set_ydata(Experiment.data[1+line_number*2][1:]) - self.lines[line_number].set_xdata(Experiment.data[line_number*2][1:]) + divisor = len(Experiment.data[1+line_number*2]) // 2000 + 1 #limits display to 2000 data points per line + + self.lines[line_number].set_ydata(Experiment.data[1+line_number*2][1::divisor]) + self.lines[line_number].set_xdata(Experiment.data[line_number*2][1::divisor]) def changetype(self, Experiment): self.axe1.set_xlabel(Experiment.xlabel) diff --git a/dstatInterface/setup.py b/dstatInterface/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..56b57744d1147231a61ce30fe48bb1c36b264c39 --- /dev/null +++ b/dstatInterface/setup.py @@ -0,0 +1,6 @@ +from distutils.core import setup +from Cython.Build import cythonize + +setup( + ext_modules = cythonize("*.pyx") + ) \ No newline at end of file