From 6fae1d39a21079d8d29ce6159407978bad5f75a8 Mon Sep 17 00:00:00 2001 From: "Michael D. M. Dryden" Date: Mon, 30 Nov 2015 15:21:27 -0500 Subject: [PATCH] Automatic offset calibration. --- dstat_interface/dstat-interface.bat | 2 +- dstat_interface/dstat_comm.py | 244 +++++++++++++++-------- dstat_interface/interface/calib.glade | 49 +++-- dstat_interface/interface/exp_int.py | 126 +++++++++--- dstat_interface/{__main__.py => main.py} | 5 + 5 files changed, 296 insertions(+), 130 deletions(-) rename dstat_interface/{__main__.py => main.py} (99%) diff --git a/dstat_interface/dstat-interface.bat b/dstat_interface/dstat-interface.bat index 031c214..da83e27 100644 --- a/dstat_interface/dstat-interface.bat +++ b/dstat_interface/dstat-interface.bat @@ -1 +1 @@ -python .\__main__.py +python .\main.py diff --git a/dstat_interface/dstat_comm.py b/dstat_interface/dstat_comm.py index 59b15d3..d5edd59 100644 --- a/dstat_interface/dstat_comm.py +++ b/dstat_interface/dstat_comm.py @@ -79,19 +79,6 @@ class SerialConnection(object): self.proc.start() -def call_it(instance, name, args=(), kwargs=None): - """Indirect caller for instance methods and multiprocessing. - - Arguments: - instance -- instance to which the method belongs - name -- method to call - args -- passed to method - kwargs -- passed to method - """ - if kwargs is None: - kwargs = {} - return getattr(instance, name)(*args, **kwargs) - class VersionCheck: def __init__(self): pass @@ -131,7 +118,31 @@ class VersionCheck: finally: return status + +def version_check(ser_port): + """Tries to contact DStat and get version. Returns a list of + [(major, minor), serial instance]. If no response, returns empty tuple. + Arguments: + ser_port -- address of serial port to use + """ + try: + global serial_instance + serial_instance = SerialConnection(ser_port) + + serial_instance.proc_pipe_p.send(VersionCheck()) + result = serial_instance.proc_pipe_p.recv() + if result == "SERIAL_ERROR": + buffer = 1 + else: + buffer = serial_instance.data_pipe_p.recv() + print result + + return buffer + + except: + pass + class Settings: def __init__(self, task, settings=None): self.task = task @@ -201,7 +212,39 @@ class Settings: self.ser.write(' ') return - + +def read_settings(): + """Tries to contact DStat and get settings. Returns dict of + settings. + """ + + global settings + settings = {} + + while serial_instance.data_pipe_p.poll(): + serial_instance.data_pipe_p.recv() + + serial_instance.proc_pipe_p.send(Settings(task='r')) + settings = serial_instance.data_pipe_p.recv() + + print serial_instance.proc_pipe_p.recv() + + return + +def write_settings(): + """Tries to write settings to DStat from global settings var. + """ + + while serial_instance.data_pipe_p.poll(): + serial_instance.data_pipe_p.recv() + + serial_instance.proc_pipe_p.send(Settings(task='w', settings=settings)) + + while serial_instance.proc_pipe_p.recv() != "DONE": + pass + + return + class LightSensor: def __init__(self): pass @@ -238,37 +281,14 @@ class LightSensor: return status - -def version_check(ser_port): - """Tries to contact DStat and get version. Returns a list of - [(major, minor), serial instance]. If no response, returns empty tuple. - - Arguments: - ser_port -- address of serial port to use - """ - try: - global serial_instance - serial_instance = SerialConnection(ser_port) - - serial_instance.proc_pipe_p.send(VersionCheck()) - result = serial_instance.proc_pipe_p.recv() - if result == "SERIAL_ERROR": - buffer = 1 - else: - buffer = serial_instance.data_pipe_p.recv() - print result - - return buffer - - except: - pass - - def read_light_sensor(): """Tries to contact DStat and get light sensor reading. Returns uint of light sensor clear channel. """ + while serial_instance.data_pipe_p.poll(): + serial_instance.data_pipe_p.recv() + serial_instance.proc_pipe_p.send(LightSensor()) while serial_instance.proc_pipe_p.recv() != "DONE": @@ -276,32 +296,6 @@ def read_light_sensor(): return serial_instance.data_pipe_p.recv() -def read_settings(): - """Tries to contact DStat and get settings. Returns dict of - settings. - """ - - global settings - settings = {} - serial_instance.proc_pipe_p.send(Settings(task='r')) - settings = serial_instance.data_pipe_p.recv() - - while serial_instance.proc_pipe_p.recv() != "DONE": - pass - - return - -def write_settings(): - """Tries to write settings to DStat from global settings var. - """ - - serial_instance.proc_pipe_p.send(Settings(task='w')) - - while serial_instance.proc_pipe_p.recv() != "DONE": - pass - - return - class delayedSerial(serial.Serial): """Extends Serial.write so that characters are output individually @@ -329,12 +323,6 @@ class Experiment(object): """Store and acquire a potentiostat experiment. Meant to be subclassed to by different experiment types and not used instanced directly. """ - # def run_wrapper(self, *argv): - # """Execute experiment indirectly using call_it to bypass lack of fork() - # on Windows for multiprocessing. - # """ - # self.proc = mp.Process(target=call_it, args=(self, 'run', argv)) - # self.proc.start() def __init__(self, parameters): """Adds commands for gain and ADC.""" @@ -399,16 +387,14 @@ class Experiment(object): self.serial.write(i) if not self.serial_handler(): status = "ABORT" - break - + + self.data_postprocessing() except serial.SerialException: status = "SERIAL_ERROR" - finally: - self.data_postprocessing() while self.ctrl_pipe.poll(): self.ctrl_pipe.recv() - return status + return status def serial_handler(self): """Handles incoming serial transmissions from DStat. Returns False @@ -468,6 +454,91 @@ class Experiment(object): """ pass +class CALExp(Experiment): + """Offset calibration experiment""" + def __init__(self, parameters): + self.parameters = parameters + self.databytes = 8 + self.scan = 0 + self.data = [] + + self.commands = ["EA2 3 1 ", "EG", "ER"] + + self.commands[1] += str(self.parameters['gain']) + self.commands[1] += " " + self.commands[1] += "0 " + self.commands[2] += "1 32768 " + self.commands[2] += str(self.parameters['time']) + self.commands[2] += " " + self.commands[2] += "0 " # disable photodiode interlock + + def serial_handler(self): + """Handles incoming serial transmissions from DStat. Returns False + if stop button pressed and sends abort signal to instrument. Sends + data to self.data_pipe as result of self.data_handler). + """ + + try: + while True: + if self.ctrl_pipe.poll(): + print "serial_handler ctrl_pipe" + input = self.ctrl_pipe.recv() + print input + if input == ('a' or "DISCONNECT"): + self.serial.write('a') + print "ABORT pressed!" + return False + + for line in self.serial: + if self.ctrl_pipe.poll(): + if self.ctrl_pipe.recv() == 'a': + self.serial.write('a') + print "ABORT pressed" + return False + + if line.startswith('B'): + self.data.append(self.data_handler( + self.serial.read(size=self.databytes))) + + elif line.lstrip().startswith("#"): + print line + + elif line.lstrip().startswith("no"): + print line + self.serial.flushInput() + return True + + except serial.SerialException: + return False + + def data_handler(self, data): + """Takes data_input as tuple -- (scan, data). + Returns: + current + """ + + seconds, milliseconds, current = struct.unpack(' 32767): + sum = 32767 + elif (sum < -32768): + sum = -32768 + + self.data_pipe.send(sum) + class Chronoamp(Experiment): """Chronoamperometry experiment""" def __init__(self, parameters): @@ -754,4 +825,21 @@ class OCPExp(Experiment): scan, data = data_input # 2*uint16 + int32 seconds, milliseconds, voltage = struct.unpack('13 2 True + + + + + + + + + True True + 5 8 0 @@ -55,6 +65,7 @@ Measurement Time (s) + GTK_FILL @@ -78,6 +89,8 @@ 2 3 + + 10 @@ -102,6 +115,7 @@ 3 4 + @@ -113,6 +127,7 @@ 4 5 + GTK_EXPAND @@ -124,6 +139,7 @@ 5 6 + GTK_EXPAND @@ -135,6 +151,7 @@ 6 7 + GTK_EXPAND @@ -146,6 +163,7 @@ 7 8 + GTK_EXPAND @@ -157,6 +175,7 @@ 8 9 + GTK_EXPAND @@ -168,13 +187,14 @@ 9 10 + GTK_EXPAND True True - 4 + 6 8 0 @@ -199,7 +219,7 @@ True True - 4 + 6 8 0 @@ -224,7 +244,7 @@ True True - 4 + 6 8 0 @@ -249,7 +269,7 @@ True True - 4 + 6 8 0 @@ -274,7 +294,7 @@ True True - 4 + 6 8 0 @@ -299,7 +319,7 @@ True True - 4 + 6 8 0 @@ -324,7 +344,7 @@ True True - 4 + 6 8 0 @@ -385,6 +405,7 @@ True True True + 1 @@ -395,15 +416,6 @@ GTK_EXPAND | GTK_SHRINK | GTK_FILL - - - - - - - - - False @@ -415,9 +427,10 @@ True False - 20 + 5 20 - Measure with WE open. Offsets cannot exceed 16 bit unsigned int. + Measure with WE open. +Offsets cannot exceed 16 bit unsigned int. diff --git a/dstat_interface/interface/exp_int.py b/dstat_interface/interface/exp_int.py index 682d375..edc13d0 100644 --- a/dstat_interface/interface/exp_int.py +++ b/dstat_interface/interface/exp_int.py @@ -262,40 +262,100 @@ class CAL(ExpInterface): self.entry['R30M'] = self.builder.get_object('30M_entry') self.entry['R100M'] = self.builder.get_object('100M_entry') - def on_read_button_clicked(self, data=None): - __main__.MAIN.on_pot_stop_clicked() + self.buttons = [self.builder.get_object('read_button'), + self.builder.get_object('write_button'), + self.builder.get_object('measure_button')] - gobject.source_remove(__main__.MAIN.ocp_proc) - dstat_comm.read_settings() - - self.entry['R100'].set_text(str( - dstat_comm.settings['r100_trim'][1])) - self.entry['R3k'].set_text(str( - dstat_comm.settings['r3k_trim'][1])) - self.entry['R30k'].set_text(str( - dstat_comm.settings['r30k_trim'][1])) - self.entry['R300k'].set_text(str( - dstat_comm.settings['r300k_trim'][1])) - self.entry['R3M'].set_text(str( - dstat_comm.settings['r3M_trim'][1])) - self.entry['R30M'].set_text(str( - dstat_comm.settings['r30M_trim'][1])) - self.entry['R100M'].set_text(str( - dstat_comm.settings['r100M_trim'][1])) - - __main__.MAIN.start_ocp() + def on_read_button_clicked(self, data=None): + for i in self.buttons: + i.set_sensitive(False) + + try: + __main__.MAIN.on_pot_stop_clicked() + gobject.source_remove(__main__.MAIN.ocp_proc) + dstat_comm.read_settings() + + self.entry['R100'].set_text(str( + dstat_comm.settings['r100_trim'][1])) + self.entry['R3k'].set_text(str( + dstat_comm.settings['r3k_trim'][1])) + self.entry['R30k'].set_text(str( + dstat_comm.settings['r30k_trim'][1])) + self.entry['R300k'].set_text(str( + dstat_comm.settings['r300k_trim'][1])) + self.entry['R3M'].set_text(str( + dstat_comm.settings['r3M_trim'][1])) + self.entry['R30M'].set_text(str( + dstat_comm.settings['r30M_trim'][1])) + self.entry['R100M'].set_text(str( + dstat_comm.settings['r100M_trim'][1])) + + __main__.MAIN.start_ocp() + + finally: + for i in self.buttons: + i.set_sensitive(True) def on_write_button_clicked(self, data=None): - __main__.MAIN.on_pot_stop_clicked() - gobject.source_remove(__main__.MAIN.ocp_proc) + for i in self.buttons: + i.set_sensitive(False) + + try: + __main__.MAIN.on_pot_stop_clicked() + gobject.source_remove(__main__.MAIN.ocp_proc) + + dstat_comm.settings['r100_trim'][1] = self.entry['R100'].get_text() + dstat_comm.settings['r3k_trim'][1] = self.entry['R3k'].get_text() + dstat_comm.settings['r30k_trim'][1] = self.entry['R30k'].get_text() + dstat_comm.settings['r300k_trim'][1] = self.entry['R300k'].get_text() + dstat_comm.settings['r3M_trim'][1] = self.entry['R3M'].get_text() + dstat_comm.settings['r30M_trim'][1] = self.entry['R30M'].get_text() + dstat_comm.settings['r100M_trim'][1] = self.entry['R100M'].get_text() + dstat_comm.write_settings() + + __main__.MAIN.start_ocp() + + finally: + for i in self.buttons: + i.set_sensitive(True) + + def on_measure_button_clicked(self, data=None): + if (int(self.entry['time'].get_text()) <= 0 or int(self.entry['time'].get_text()) > 65535): + print "ERR: Time out of range" + return + + for i in self.buttons: + i.set_sensitive(False) + + try: + __main__.MAIN.on_pot_stop_clicked() + gobject.source_remove(__main__.MAIN.ocp_proc) + __main__.MAIN.spinner.start() + + offset = dstat_comm.measure_offset(self.get_params()['time']) + + for i in offset: + print i + print str(-offset[i]) + dstat_comm.settings[i][1] = str(-offset[i]) + + self.entry['R100'].set_text(str( + dstat_comm.settings['r100_trim'][1])) + self.entry['R3k'].set_text(str( + dstat_comm.settings['r3k_trim'][1])) + self.entry['R30k'].set_text(str( + dstat_comm.settings['r30k_trim'][1])) + self.entry['R300k'].set_text(str( + dstat_comm.settings['r300k_trim'][1])) + self.entry['R3M'].set_text(str( + dstat_comm.settings['r3M_trim'][1])) + self.entry['R30M'].set_text(str( + dstat_comm.settings['r30M_trim'][1])) + self.entry['R100M'].set_text(str( + dstat_comm.settings['r100M_trim'][1])) + __main__.MAIN.start_ocp() - dstat_comm.settings['r100_trim'][1] = self.entry['R100'].get_text() - dstat_comm.settings['r3k_trim'][1] = self.entry['R3k'].get_text() - dstat_comm.settings['r30k_trim'][1] = self.entry['R30k'].get_text() - dstat_comm.settings['r300k_trim'][1] = self.entry['R300k'].get_text() - dstat_comm.settings['r3M_trim'][1] = self.entry['R3M'].get_text() - dstat_comm.settings['r30M_trim'][1] = self.entry['R30M'].get_text() - dstat_comm.settings['r100M_trim'][1] = self.entry['R100M'].get_text() - dstat_comm.write_settings() - - __main__.MAIN.start_ocp() \ No newline at end of file + finally: + for i in self.buttons: + i.set_sensitive(True) + __main__.MAIN.spinner.stop() \ No newline at end of file diff --git a/dstat_interface/__main__.py b/dstat_interface/main.py similarity index 99% rename from dstat_interface/__main__.py rename to dstat_interface/main.py index 6461973..fbf795c 100755 --- a/dstat_interface/__main__.py +++ b/dstat_interface/main.py @@ -731,6 +731,8 @@ class Main(object): for i in self.current_exp.commands: self.rawbuffer.insert_at_cursor(i) + self.rawbuffer.insert_at_cursor("\n") + for col in zip(*self.current_exp.data): for row in col: self.rawbuffer.insert_at_cursor(str(row)+ " ") @@ -769,6 +771,9 @@ class Main(object): comm.serial_instance.ctrl_pipe_p.send('a') while not (comm.serial_instance.proc_pipe_p.recv() == "ABORT"): pass + + while comm.serial_instance.data_pipe_p.poll(): + comm.serial_instance.data_pipe_p.recv() except AttributeError: pass except: -- GitLab