diff --git a/.gitignore b/.gitignore index a819e33815ef5594cde4f4585d3266d75150fa5c..868139ac84a3c9d54009d39932c400f70aa6b886 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ paver-minilib.zip dist setup.py *.egg-info +last_params +last_params.yml diff --git a/dstat_interface/dstat_comm.py b/dstat_interface/dstat_comm.py old mode 100644 new mode 100755 index 4667581b3ea166bf63e754724b0786194055e894..0e8469b73f40ec0539de0da8e45b9f40c0a36cb1 --- a/dstat_interface/dstat_comm.py +++ b/dstat_interface/dstat_comm.py @@ -355,7 +355,7 @@ class Experiment(object): self.__gaintable = [1e2, 3e2, 3e3, 3e4, 3e5, 3e6, 3e7, 5e8] elif minor >= 2: self.__gaintable = [1, 1e2, 3e3, 3e4, 3e5, 3e6, 3e7, 1e8] - self.__gain_trim_table = [None, 'r100_trim', 'r3k_trim', + self.__gain_trim_table = ['r100_trim', 'r100_trim', 'r3k_trim', 'r30k_trim', 'r300k_trim', 'r3M_trim', 'r30M_trim', 'r100M_trim'] else: @@ -366,8 +366,11 @@ class Experiment(object): settings[self.__gain_trim_table[int(self.parameters['gain'])]][1]) self.commands = ["EA", "EG"] - - self.commands[0] += (self.parameters['adc_buffer']) + + if self.parameters['buffer_true']: + self.commands[0] += "2" + else: + self.commands[0] += "0" self.commands[0] += " " self.commands[0] += (self.parameters['adc_rate']) self.commands[0] += " " @@ -375,7 +378,7 @@ class Experiment(object): self.commands[0] += " " self.commands[1] += (self.parameters['gain']) self.commands[1] += " " - self.commands[1] += (self.parameters['re_short']) + self.commands[1] += (str(int(self.parameters['short_true']))) self.commands[1] += " " def run(self, ser, ctrl_pipe, data_pipe): @@ -610,10 +613,10 @@ class PDExp(Chronoamp): self.datalength = 2 self.databytes = 8 self.xmin = 0 - self.xmax = self.parameters['time'] + self.xmax = int(self.parameters['time']) - if self.parameters['shutter']: - if self.parameters['sync']: + if self.parameters['shutter_true']: + if self.parameters['sync_true']: self.commands.append("EZ") self.commands[-1] += str(self.parameters['sync_freq']) self.commands[-1] += " " @@ -630,14 +633,14 @@ class PDExp(Chronoamp): self.commands[-1] += " " self.commands[-1] += str(self.parameters['time']) self.commands[-1] += " " - if self.parameters['interlock']: + if self.parameters['interlock_true']: self.commands[-1] += "1" else: self.commands[-1] += "0" self.commands[-1] += " " - if self.parameters['shutter']: - if self.parameters['sync']: + if self.parameters['shutter_true']: + if self.parameters['sync_true']: self.commands.append("Ez") else: self.commands.append("E1") @@ -680,8 +683,8 @@ class LSVExp(Experiment): self.data = [[], []] self.datalength = 2 self.databytes = 6 # uint16 + int32 - self.xmin = self.parameters['start'] - self.xmax = self.parameters['stop'] + self.xmin = int(self.parameters['start']) + self.xmax = int(self.parameters['stop']) self.commands += "E" self.commands[2] += "L" @@ -689,10 +692,10 @@ class LSVExp(Experiment): self.commands[2] += " " self.commands[2] += str(self.parameters['dep_s']) self.commands[2] += " " - self.commands[2] += str(int(self.parameters['clean_mV']* + self.commands[2] += str(int(int(self.parameters['clean_mV'])* (65536./3000)+32768)) self.commands[2] += " " - self.commands[2] += str(int(self.parameters['dep_mV']* + self.commands[2] += str(int(int(self.parameters['dep_mV'])* (65536./3000)+32768)) self.commands[2] += " " self.commands[2] += str(self.parameters['start']) @@ -713,8 +716,8 @@ class CVExp(Experiment): self.data = [[], []] self.datalength = 2 * self.parameters['scans'] # x and y for each scan self.databytes = 6 # uint16 + int32 - self.xmin = self.parameters['v1'] - self.xmax = self.parameters['v2'] + self.xmin = int(self.parameters['v1']) + self.xmax = int(self.parameters['v2']) self.commands += "E" self.commands[2] += "C" @@ -722,10 +725,10 @@ class CVExp(Experiment): self.commands[2] += " " self.commands[2] += str(self.parameters['dep_s']) self.commands[2] += " " - self.commands[2] += str(int(self.parameters['clean_mV']* + self.commands[2] += str(int(int(self.parameters['clean_mV'])* (65536./3000)+32768)) self.commands[2] += " " - self.commands[2] += str(int(self.parameters['dep_mV']* + self.commands[2] += str(int(int(self.parameters['dep_mV'])* (65536./3000)+32768)) self.commands[2] += " " self.commands[2] += str(self.parameters['v1']) @@ -751,8 +754,8 @@ class SWVExp(Experiment): self.datalength = 2 * self.parameters['scans'] self.databytes = 10 - self.xmin = self.parameters['start'] - self.xmax = self.parameters['stop'] + self.xmin = int(self.parameters['start']) + self.xmax = int(self.parameters['stop']) self.data_extra = [[], []] @@ -762,11 +765,11 @@ class SWVExp(Experiment): 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] += str(int(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] += str(int(int(self.parameters['dep_mV'])* + (65536./3000)+32768)) self.commands[2] += " " self.commands[2] += str(self.parameters['start']) self.commands[2] += " " @@ -808,8 +811,8 @@ class DPVExp(SWVExp): self.datalength = 2 self.databytes = 10 - self.xmin = self.parameters['start'] - self.xmax = self.parameters['stop'] + self.xmin = int(self.parameters['start']) + self.xmax = int(self.parameters['stop']) self.data_extra = [[], []] @@ -819,11 +822,11 @@ class DPVExp(SWVExp): 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] += str(int(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] += str(int(int(self.parameters['dep_mV'])* + (65536./3000)+32768)) self.commands[2] += " " self.commands[2] += str(self.parameters['start']) self.commands[2] += " " diff --git a/dstat_interface/interface/adc_pot.py b/dstat_interface/interface/adc_pot.py old mode 100644 new mode 100755 index 6fdbd598d6b09238110150b51cff247779738241..0a0561680c5c4008a5d854ba8727da28c3994183 --- a/dstat_interface/interface/adc_pot.py +++ b/dstat_interface/interface/adc_pot.py @@ -20,6 +20,9 @@ import gtk +from errors import InputError, VarError, ErrorLogger +_logger = ErrorLogger(sender="dstat_adc_pot") + v1_1_gain = [(0, "100 Ω (15 mA FS)", 0), (1, "300 Ω (5 mA FS)", 1), (2, "3 kΩ (500 µA FS)", 2), @@ -37,34 +40,98 @@ v1_2_gain = [(0, "Bypass", 0), (5, "3 MΩ (500 nA FS)", 5), (6, "30 MΩ (50 nA FS)", 6), (7, "100 MΩ (15 nA FS)", 7)] + -class adc_pot: +class adc_pot(object): def __init__(self): self.builder = gtk.Builder() self.builder.add_from_file('interface/adc_pot.glade') self.builder.connect_signals(self) self.cell = gtk.CellRendererText() - self.buffer_toggle = self.builder.get_object('buffer_checkbutton') - self.short_toggle = self.builder.get_object('short_checkbutton') - + ui_keys = ['buffer_true', + 'short_true', + 'pga_index', + 'srate_index', + 'gain_index' + ] + ui_cont = map(self.builder.get_object, ['buffer_checkbutton', + 'short_checkbutton', + 'pga_combobox', + 'srate_combobox', + 'gain_combobox' + ] + ) + self.ui = dict(zip(ui_keys, ui_cont)) + #initialize comboboxes - self.pga_combobox = self.builder.get_object('pga_combobox') - self.pga_combobox.pack_start(self.cell, True) - self.pga_combobox.add_attribute(self.cell, 'text', 1) - self.pga_combobox.set_active(1) + self.ui['pga_index'].pack_start(self.cell, True) + self.ui['pga_index'].add_attribute(self.cell, 'text', 1) + self.ui['pga_index'].set_active(1) - self.srate_combobox = self.builder.get_object('srate_combobox') - self.srate_combobox.pack_start(self.cell, True) - self.srate_combobox.add_attribute(self.cell, 'text', 1) - self.srate_combobox.set_active(7) + self.ui['srate_index'].pack_start(self.cell, True) + self.ui['srate_index'].add_attribute(self.cell, 'text', 1) + self.ui['srate_index'].set_active(7) - self.gain_combobox = self.builder.get_object('gain_combobox') self.gain_liststore = self.builder.get_object('gain_liststore') - self.gain_combobox.pack_start(self.cell, True) - self.gain_combobox.add_attribute(self.cell, 'text', 1) - self.gain_combobox.set_active(2) + self.ui['gain_index'].pack_start(self.cell, True) + self.ui['gain_index'].add_attribute(self.cell, 'text', 1) + self.ui['gain_index'].set_active(2) + + self._params = {} + + @property + def params(self): + """Dict of parameters.""" + try: + self._get_params() + except InputError as e: + raise e + finally: + return self._params + + def _get_params(self): + """Updates self._params from UI.""" + for i in self.ui: + self._params[i] = self.ui[i].get_active() + + srate_model = self.ui['srate_index'].get_model() + self._params['adc_rate'] = srate_model[self._params['srate_index']][2] + srate = srate_model[self._params['srate_index']][1] + + if srate.endswith("kHz"): + sample_rate = float(srate.rstrip(" kHz"))*1000 + else: + sample_rate = float(srate.rstrip(" Hz")) + self._params['adc_rate_hz'] = sample_rate + + pga_model = self.ui['srate_index'].get_model() + self._params['adc_pga'] = pga_model[self._params['pga_index']][2] + + gain_model = self.ui['gain_index'].get_model() + self._params['gain'] = gain_model[self._params['gain_index']][2] + if self._params['gain_index'] not in range(len(gain_model)): + raise InputError(self._params['gain_index'], + "Select a potentiostat gain.") + + @params.setter + def params(self, params): + if self._params is {}: + self._params = dict.fromkeys(self.ui.keys()) + + for i in self._params: + try: + self._params[i] = params[i] + except KeyError as e: + _logger.error("Invalid parameter key: %s" % e, "WAR") + self._set_params() + + def _set_params(self): + """Updates UI with new parameters.""" + for i in self.ui: + self.ui[i].set_active(self._params[i]) + def set_version(self, version): """ Sets menus for DStat version. """ self.gain_liststore.clear() @@ -75,4 +142,5 @@ class adc_pot: elif version[1] >= 2: for i in v1_2_gain: self.gain_liststore.append(i) - \ No newline at end of file + + \ No newline at end of file diff --git a/dstat_interface/interface/dstatinterface.glade b/dstat_interface/interface/dstatinterface.glade index 62df9c0df9852d8742b1f5dbb3432243d016c357..dc598cee90b4821b36b2cde4b743378830c713c6 100644 --- a/dstat_interface/interface/dstatinterface.glade +++ b/dstat_interface/interface/dstatinterface.glade @@ -734,6 +734,16 @@ Thanks to Christian Fobel for help with Dropbot Plugin False gtk-save + + True + False + gtk-save + + + True + False + gtk-open + @@ -785,6 +795,26 @@ Thanks to Christian Fobel for help with Dropbot Plugin + + + Save Parameters… + True + False + image4 + False + + + + + + Load Parameters… + True + False + image5 + False + + + gtk-quit diff --git a/dstat_interface/interface/exp_int.py b/dstat_interface/interface/exp_int.py old mode 100644 new mode 100755 index 2775dc7acd8dad05320f21bce94d704f863b8d4e..1cb9a345d2fc8befd21fb33f289f055c12556e77 --- a/dstat_interface/interface/exp_int.py +++ b/dstat_interface/interface/exp_int.py @@ -17,33 +17,59 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os, sys +import os +import sys + import gtk +import gobject + import dstat_comm import __main__ -import gobject from errors import InputError, VarError, ErrorLogger _logger = ErrorLogger(sender="dstat-interface-exp_int") class ExpInterface(object): """Generic experiment interface class. Should be subclassed to implement experiment interfaces by populating self.entry. - - Public methods: - get_params(self) """ def __init__(self, glade_path): self.builder = gtk.Builder() self.builder.add_from_file(glade_path) self.builder.connect_signals(self) - self.entry = {} + self.entry = {} # to be used only for str parameters + self._params = None + + def _fill_params(self): + self._params = dict.fromkeys(self.entry.keys()) + + @property + def params(self): + """Dict of parameters""" + if self._params == None: + self._fill_params() + self._get_params() + return self._params + + def _get_params(self): + """Updates self._params from UI.""" + for i in self.entry: + self._params[i] = self.entry[i].get_text() + + @params.setter + def params(self, params): + if self._params == None: + self._fill_params() + for i in self._params: + try: + self._params[i] = params[i] + except KeyError as e: + _logger.error("Invalid parameter key: %s" % e, "WAR") + self._set_params() - def get_params(self): - """Returns a dict of parameters for experiment.""" - parameters = {} - for key, value in self.entry.iteritems(): - parameters[key] = int(value.get_text()) - return parameters + def _set_params(self): + """Updates UI with new parameters.""" + for i in self.entry: + self.entry[i].set_text(self._params[i]) class Chronoamp(ExpInterface): """Experiment class for chronoamperometry. Extends ExpInterface class to @@ -71,6 +97,11 @@ class Chronoamp(ExpInterface): self.selection = self.treeview.get_selection() self.selection.set_mode(gtk.SELECTION_MULTIPLE) + def _fill_params(self): + super(Chronoamp, self)._fill_params() + self._params['potential'] = [] + self._params['time'] = [] + def on_add_button_clicked(self, widget): """Add current values in potential_entry and time_entry to model.""" @@ -105,15 +136,21 @@ class Chronoamp(ExpInterface): for i in referencelist: self.model.remove(self.model.get_iter(i.get_path())) - def get_params(self): - """Returns a dict of parameters for experiment. Overrides superclass - method. - """ - parameters = {} - parameters['potential'] = [int(r[0]) for r in self.model] - parameters['time'] = [int(r[1]) for r in self.model] - - return parameters + def _get_params(self): + """Updates self._params from UI. Overrides superclass method.""" + + self._params['potential'] = [int(r[0]) for r in self.model] + self._params['time'] = [int(r[1]) for r in self.model] + + def _set_params(self): + """Updates UI from self._params. Overrides superclass method.""" + + self.model.clear() + + table = zip(self._params['potential'], self._params['time']) + + for i in table: + self.model.append(i) class LSV(ExpInterface): """Experiment class for LSV.""" @@ -161,15 +198,25 @@ class SWV(ExpInterface): self.entry['pulse'] = self.builder.get_object('pulse_entry') self.entry['freq'] = self.builder.get_object('freq_entry') self.entry['scans'] = self.builder.get_object('scans_entry') + + def _fill_params(self): + super(SWV, self)._fill_params() - def get_params(self): - """Extends superclass method to pass status of cyclic_checkbutton""" - parameters = {} - parameters['cyclic_checkbutton'] = self.builder.get_object( - 'cyclic_checkbutton').get_active() - parameters.update(super(SWV, self).get_params()) + self._params['cyclic_true'] = False + + def _get_params(self): + """Updates self._params from UI.""" + super(SWV, self)._get_params() - return parameters + self._params['cyclic_true'] = self.builder.get_object( + 'cyclic_checkbutton').get_active() + + def _set_params(self): + """Updates UI with new parameters.""" + super(SWV, self)._set_params() + + self.builder.get_object('cyclic_checkbutton').set_active( + self._params['cyclic_true']) class DPV(ExpInterface): """Experiment class for DPV.""" @@ -206,11 +253,7 @@ class PD(ExpInterface): """Adds entry listings to superclass's self.entry dict""" super(PD, self).__init__('interface/pd.glade') - self.entry['voltage'] = self.builder.get_object('voltage_adjustment') self.entry['time'] = self.builder.get_object('time_entry') - self.entry['interlock'] = self.builder.get_object('interlock_button') - self.entry['shutter'] = self.builder.get_object('shutter_button') - self.entry['sync'] = self.builder.get_object('sync_button') self.entry['sync_freq'] = self.builder.get_object('sync_freq') self.entry['fft_start'] = self.builder.get_object('fft_entry') self.entry['fft_int'] = self.builder.get_object('fft_int_entry') @@ -224,6 +267,41 @@ class PD(ExpInterface): 'fft_int_entry'] ) + bool_keys = ['interlock_true', 'shutter_true', 'sync_true'] + bool_cont = map(self.builder.get_object, + ['interlock_button', + 'shutter_button', + 'sync_button'] + ) + self.bool = dict(zip(bool_keys, bool_cont)) + + def _fill_params(self): + super(PD, self)._fill_params() + + for i in self.bool: + self._params[i] = self.bool[i].get_active() + self._params['voltage'] = 0 + + def _get_params(self): + """Updates self._params from UI.""" + super(PD, self)._get_params() + + for i in self.bool: + self._params[i] = self.bool[i].get_active() + + self._params['voltage'] = self.builder.get_object( + 'voltage_adjustment').get_value() + + def _set_params(self): + """Updates UI with new parameters.""" + super(PD, self)._set_params() + + for i in self.bool: + self.bool[i].set_active(self._params[i]) + + self.builder.get_object('voltage_adjustment').set_value( + self._params['voltage'] ) + def on_light_button_clicked(self, data=None): __main__.MAIN.on_pot_stop_clicked() __main__.MAIN.stop_ocp() @@ -265,26 +343,13 @@ class PD(ExpInterface): gobject.timeout_add(700, restore_buttons, self.buttons) def on_shutter_button_toggled(self, widget): - if self.entry['shutter'].get_active(): + if self.bool['shutter_true'].get_active(): for i in self.shutter_buttons: i.set_sensitive(True) else: for i in self.shutter_buttons: i.set_sensitive(False) - def get_params(self): - """Returns a dict of parameters for experiment.""" - parameters = {} - parameters['voltage'] = int(self.entry['voltage'].get_value()) - parameters['time'] = int(self.entry['time'].get_text()) - parameters['interlock'] = self.entry['interlock'].get_active() - parameters['shutter'] = self.entry['shutter'].get_active() - parameters['sync'] = self.entry['sync'].get_active() - parameters['sync_freq'] = float(self.entry['sync_freq'].get_text()) - parameters['fft_start'] = float(self.entry['fft_start'].get_text()) - parameters['fft_int'] = float(self.entry['fft_int'].get_text()) - - return parameters class POT(ExpInterface): """Experiment class for Potentiometry.""" diff --git a/dstat_interface/interface/exp_window.py b/dstat_interface/interface/exp_window.py old mode 100644 new mode 100755 index 0aebdb5fd2dc3290c6c465965182ffc0e032fc0a..10fa69c5c9ba08ad73ea33022c597265bf3674cb --- a/dstat_interface/interface/exp_window.py +++ b/dstat_interface/interface/exp_window.py @@ -23,28 +23,33 @@ class Experiments: def __init__(self, builder): self.builder = builder + # (experiment index in UI, experiment) self.classes = {} - self.classes['cae'] = exp.Chronoamp() - self.classes['lsv'] = exp.LSV() - self.classes['cve'] = exp.CV() - self.classes['swv'] = exp.SWV() - self.classes['dpv'] = exp.DPV() - self.classes['acv'] = exp.ACV() - self.classes['pde'] = exp.PD() - self.classes['pot'] = exp.POT() - self.classes['cal'] = exp.CAL() + self.classes['cae'] = (0, exp.Chronoamp()) + self.classes['lsv'] = (1, exp.LSV()) + self.classes['cve'] = (2, exp.CV()) + self.classes['swv'] = (3, exp.SWV()) + self.classes['dpv'] = (4, exp.DPV()) + self.classes['acv'] = (5, exp.ACV()) + self.classes['pde'] = (6, exp.PD()) + self.classes['pot'] = (7, exp.POT()) + self.classes['cal'] = (8, exp.CAL()) + + # Create reverse lookup + self.select_to_key = {} + for key, value in self.classes.iteritems(): + self.select_to_key[value[0]] = key #fill exp_section exp_section = self.builder.get_object('exp_section_box') self.containers = {} for key, cls in self.classes.iteritems(): - self.containers[key] = cls.builder.get_object('scrolledwindow1') + self.containers[key] = cls[1].builder.get_object('scrolledwindow1') for key in self.containers: self.containers[key].reparent(exp_section) self.containers[key].hide() - def set_exp(self, selection): """Changes parameter tab to selected experiment. Returns True if @@ -57,8 +62,12 @@ class Experiments: self.containers[key].hide() self.containers[selection].show() + self.selected_exp = selection return True def get_params(self, experiment): - return self.classes[experiment].get_params() \ No newline at end of file + return self.classes[experiment][1].params + + def set_params(self, experiment, parameters): + self.classes[experiment][1].params = parameters \ No newline at end of file diff --git a/dstat_interface/interface/save.py b/dstat_interface/interface/save.py old mode 100644 new mode 100755 index 85248cb56af6db9cb8c98a7d922e4a73111ae18e..b260b87404c1da036c99d344e049d85f79dc6503 --- a/dstat_interface/interface/save.py +++ b/dstat_interface/interface/save.py @@ -18,11 +18,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import gtk, io, os +import io +import os + +import gtk import numpy as np from errors import InputError, VarError, ErrorLogger _logger = ErrorLogger(sender="dstat-interface-save") +from params import save_params, load_params def manSave(current_exp): fcd = gtk.FileChooserDialog("Save...", None, gtk.FILE_CHOOSER_ACTION_SAVE, @@ -106,6 +110,66 @@ def plotSave(plots): elif response == gtk.RESPONSE_CANCEL: fcd.destroy() +def man_param_save(window): + fcd = gtk.FileChooserDialog("Save Parameters…", + None, + gtk.FILE_CHOOSER_ACTION_SAVE, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_SAVE, gtk.RESPONSE_OK) + ) + + filters = [gtk.FileFilter()] + filters[0].set_name("Parameter File (.yml)") + filters[0].add_pattern("*.yml") + + fcd.set_do_overwrite_confirmation(True) + for i in filters: + fcd.add_filter(i) + + response = fcd.run() + + if response == gtk.RESPONSE_OK: + path = fcd.get_filename() + _logger.error(" ".join(("Selected filepath:", path)),'INFO') + + if not path.endswith(".yml"): + path += '.yml' + + save_params(window, path) + + fcd.destroy() + + elif response == gtk.RESPONSE_CANCEL: + fcd.destroy() + +def man_param_load(window): + fcd = gtk.FileChooserDialog("Load Parameters…", + None, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_OK) + ) + + filters = [gtk.FileFilter()] + filters[0].set_name("Parameter File (.yml)") + filters[0].add_pattern("*.yml") + + for i in filters: + fcd.add_filter(i) + + response = fcd.run() + + if response == gtk.RESPONSE_OK: + path = fcd.get_filename() + _logger.error(" ".join(("Selected filepath:", path)),'INFO') + + load_params(window, path) + + fcd.destroy() + + elif response == gtk.RESPONSE_CANCEL: + fcd.destroy() + def autoSave(current_exp, dir_button, name, expnumber): if name == "": name = "file" diff --git a/dstat_interface/main.py b/dstat_interface/main.py index f5317434a3e303dd0962632ae48238b612de2caf..a71bede88de7b949eff6ad4b85240bcd1e4efee1 100755 --- a/dstat_interface/main.py +++ b/dstat_interface/main.py @@ -43,6 +43,7 @@ except ImportError: print "ERR: gobject not available" sys.exit(1) from serial import SerialException +from datetime import datetime os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) @@ -53,11 +54,11 @@ import interface.exp_window as exp_window import interface.adc_pot as adc_pot import plot import microdrop +import params +import parameter_test from errors import InputError, VarError, ErrorLogger _logger = ErrorLogger(sender="dstat-interface-main") -from datetime import datetime - class Main(object): """Main program """ def __init__(self): @@ -158,14 +159,22 @@ class Main(object): self.plot_notebook.page_num(self.ft_window)).hide() self.plot_notebook.get_nth_page( self.plot_notebook.page_num(self.period_window)).hide() + + self.params_loaded = False + def on_window1_destroy(self, object, data=None): """ Quit when main window closed.""" - self.on_serial_disconnect_clicked() - gtk.main_quit() + self.quit() def on_gtk_quit_activate(self, menuitem, data=None): """Quit when Quit selected from menu.""" + self.quit() + + def quit(self): + """Disconnect and save parameters on quit.""" + params.save_params(self, 'last_params.yml') + self.on_serial_disconnect_clicked() gtk.main_quit() @@ -202,13 +211,15 @@ class Main(object): self.statusbar.remove_all(self.error_context_id) if not len(self.version) == 2: - self.statusbar.push(self.error_context_id, "Communication Error") + self.statusbar.push(self.error_context_id, + "Communication Error") return else: self.adc_pot.set_version(self.version) self.statusbar.push(self.error_context_id, - "".join(["DStat version: ", str(self.version[0]), + "".join(["DStat version: ", + str(self.version[0]), ".", str(self.version[1])]) ) @@ -226,6 +237,12 @@ class Main(object): except TypeError as err: _logger.error(err, 'WAR') self.serial_connect.set_sensitive(True) + + if self.params_loaded == False: + try: + params.load_params(self, 'last_params.yml') + except IOError: + _logger.error("No previous parameters found.", 'INFO') def on_serial_disconnect_clicked(self, data=None): """Disconnect from DStat.""" @@ -249,13 +266,13 @@ class Main(object): self.serial_connect.set_sensitive(True) self.serial_pmt_connect.set_sensitive(True) self.serial_disconnect.set_sensitive(False) - self.adc_pot.short_toggle.set_sensitive(True) + self.adc_pot.ui['short_true'].set_sensitive(True) def on_pmt_mode_clicked(self, data=None): """Connect in PMT mode""" self.pmt_mode = True - self.adc_pot.short_toggle.set_active(True) - self.adc_pot.short_toggle.set_sensitive(False) + self.adc_pot.ui['short_true'].set_active(True) + self.adc_pot.ui['short_true'].set_sensitive(False) self.on_serial_connect_clicked() def start_ocp(self): @@ -390,7 +407,7 @@ class Main(object): nb = self.plot_notebook - if (parameters['sync'] and parameters['shutter']): + if (parameters['sync_true'] and parameters['shutter_true']): nb.get_nth_page( nb.page_num(self.ft_window)).show() # nb.get_nth_page( @@ -401,7 +418,6 @@ class Main(object): nb.get_nth_page(nb.page_num(self.ft_window)).hide() # nb.get_nth_page(nb.page_num(self.period_window)).hide() - comm.serial_instance.proc_pipe_p.send(self.current_exp) # Flush data pipe @@ -422,66 +438,25 @@ class Main(object): while comm.serial_instance.data_pipe_p.poll(): # Clear data pipe comm.serial_instance.data_pipe_p.recv() - selection = self.expcombobox.get_active() parameters = {} parameters['version'] = self.version # Make sure these are defined - parameters['sync'] = False - parameters['shutter'] = False - - if self.adc_pot.buffer_toggle.get_active(): # True if box checked - parameters['adc_buffer'] = "2" - else: - parameters['adc_buffer'] = "0" - - if self.adc_pot.short_toggle.get_active(): - parameters['re_short'] = "1" - else: - parameters['re_short'] = "0" - - srate_model = self.adc_pot.srate_combobox.get_model() - pga_model = self.adc_pot.pga_combobox.get_model() - gain_model = self.adc_pot.gain_combobox.get_model() - - parameters['adc_rate'] = srate_model.get_value( - self.adc_pot.srate_combobox.get_active_iter(), 2) # third column - - srate = srate_model.get_value( - self.adc_pot.srate_combobox.get_active_iter(), 1) - - if srate.endswith("kHz"): - sample_rate = float(srate.rstrip(" kHz"))*1000 - else: - sample_rate = float(srate.rstrip(" Hz")) - - parameters['adc_rate_hz'] = sample_rate - parameters['adc_pga'] = pga_model.get_value( - self.adc_pot.pga_combobox.get_active_iter(), 2) - + parameters['sync_true'] = False + parameters['shutter_true'] = False try: - parameters['gain'] = gain_model.get_value( - self.adc_pot.gain_combobox.get_active_iter(), 2) - except TypeError as err: - print "TypeError" - _logger.error(err, "INFO") - self.statusbar.push(self.error_context_id, - "Select a potentiostat gain.") - exceptions() - return - - - self.line = 0 - self.lastline = 0 - self.lastdataline = 0 - - self.spinner.start() - self.startbutton.set_sensitive(False) - self.stopbutton.set_sensitive(True) - self.statusbar.remove_all(self.error_context_id) + parameters.update(self.adc_pot.params) + + self.line = 0 + self.lastline = 0 + self.lastdataline = 0 - try: + self.spinner.start() + self.startbutton.set_sensitive(False) + self.stopbutton.set_sensitive(True) + self.statusbar.remove_all(self.error_context_id) + if selection == 0: # CA # Add experiment parameters to existing parameters.update(self.exp_window.get_params('cae')) @@ -504,78 +479,16 @@ class Main(object): elif selection == 1: # LSV parameters.update(self.exp_window.get_params('lsv')) - - #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): - raise InputError(parameters['stop'], - "Stop parameter exceeds hardware limits.") - if (parameters['slope'] > 2000 or parameters['slope'] < 1): - raise InputError(parameters['slope'], - "Slope parameter exceeds hardware limits.") - if parameters['start'] == parameters['stop']: - raise InputError(parameters['start'], - "Start cannot equal Stop.") - + parameter_test.lsv_test(parameters) self.current_exp = comm.LSVExp(parameters) run_experiment() - + return elif selection == 2: # CV parameters.update(self.exp_window.get_params('cve')) - - # 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): - raise InputError(parameters['slope'], - "Slope parameter exceeds hardware limits.") - if (parameters['v1'] > 1499 or parameters['v1'] < -1500): - raise InputError(parameters['v1'], - "Vertex 1 parameter exceeds hardware limits.") - if (parameters['v2'] > 1499 or parameters['v2'] < -1500): - raise InputError(parameters['v2'], - "Vertex 2 parameter exceeds hardware limits.") - if (parameters['scans'] < 1 or parameters['scans'] > 255): - raise InputError(parameters['scans'], - "Scans parameter outside limits.") - if parameters['v1'] == parameters['v2']: - raise InputError(parameters['v1'], - "Vertex 1 cannot equal Vertex 2.") - + parameter_test.cv_test(parameters) self.current_exp = comm.CVExp(parameters) run_experiment() @@ -584,49 +497,7 @@ class Main(object): elif selection == 3: # SWV parameters.update(self.exp_window.get_params('swv')) - - if parameters['cyclic_checkbutton'] : - 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): - raise InputError(parameters['step'], - "Step height parameter exceeds hardware limits.") - if (parameters['stop'] > 1499 or parameters['stop'] < -1500): - raise InputError(parameters['stop'], - "Stop parameter exceeds hardware limits.") - if (parameters['pulse'] > 150 or parameters['pulse'] < 1): - raise InputError(parameters['pulse'], - "Pulse height parameter exceeds hardware limits.") - if (parameters['freq'] < 1 or parameters['freq'] > 1000): - raise InputError(parameters['freq'], - "Frequency parameter outside limits.") - if parameters['start'] == parameters['stop']: - raise InputError(parameters['start'], - "Start cannot equal Stop.") + parameter_test.swv_test(parameters) self.current_exp = comm.SWVExp(parameters) run_experiment() @@ -635,45 +506,7 @@ class Main(object): elif selection == 4: # DPV parameters.update(self.exp_window.get_params('dpv')) - - 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): - raise InputError(parameters['step'], - "Step height parameter exceeds hardware limits.") - if (parameters['stop'] > 1499 or parameters['stop'] < -1500): - raise InputError(parameters['stop'], - "Stop parameter exceeds hardware limits.") - if (parameters['pulse'] > 150 or parameters['pulse'] < 1): - raise InputError(parameters['pulse'], - "Pulse height parameter exceeds hardware limits.") - if (parameters['period'] < 1 or parameters['period'] > 1000): - raise InputError(parameters['period'], - "Period parameter outside limits.") - if (parameters['width'] < 1 or parameters['width'] > 1000): - raise InputError(parameters['width'], - "Width parameter outside limits.") - if parameters['period'] <= parameters['width']: - raise InputError(parameters['width'], - "Width must be less than period.") - if parameters['start'] == parameters['stop']: - raise InputError(parameters['start'], - "Start cannot equal Stop.") + parameter_test.dpv_test(parameters) self.current_exp = comm.DPVExp(parameters) run_experiment() @@ -682,27 +515,7 @@ class Main(object): elif selection == 6: # PD parameters.update(self.exp_window.get_params('pde')) - - if (parameters['time'] <= 0): - raise InputError(parameters['clean_s'], - "Time must be greater than zero.") - if (parameters['time'] > 65535): - raise InputError(parameters['clean_s'], - "Time must fit in 16-bit counter.") - if (parameters['sync'] and parameters['shutter']): - if (parameters['sync_freq'] > 30 or - parameters['sync_freq'] <= 0): - raise InputError(parameters['sync_freq'], - "Frequency must be between 0 and 30 Hz.") - if (parameters['fft_start'] < 0 or - parameters['fft_start'] > parameters['time']-1): - raise InputError(parameters['fft_start'], - "FFT must start between 0 and time-1.") - if parameters['fft_int'] < 0: - raise InputError( - parameters['fft_int'], - "Integral bandwidth must be greater than 0" - ) + parameter_test.pd_test(parameters) self.current_exp = comm.PDExp(parameters) run_experiment() @@ -717,14 +530,7 @@ class Main(object): return parameters.update(self.exp_window.get_params('pot')) - - if (parameters['time'] <= 0): - raise InputError(parameters['clean_s'], - "Time must be greater than zero.") - if (parameters['time'] > 65535): - raise InputError(parameters['clean_s'], - "Time must fit in 16-bit counter.") - + parameter_test.pot_test(parameters) self.current_exp = comm.PotExp(parameters) run_experiment() @@ -743,8 +549,8 @@ class Main(object): exceptions() except KeyError as i: - _logger.error(i, "INFO") - self.statusbar.push(self.error_context_id, + _logger.error("KeyError: %s" % i, "INFO") + self.statusbar.push(self.error_context_id, "Experiment parameters must be integers.") exceptions() @@ -867,8 +673,8 @@ class Main(object): gobject.source_remove(self.plot_proc) # stop automatic plot update self.experiment_running_plot() # make sure all data updated on plot - if (self.current_exp.parameters['shutter'] and - self.current_exp.parameters['sync']): + if (self.current_exp.parameters['shutter_true'] and + self.current_exp.parameters['sync_true']): self.ft_plot.updateline(self.current_exp, 0) self.ft_plot.redraw() self.current_exp.data_extra = self.current_exp.ftdata @@ -905,8 +711,8 @@ class Main(object): self.autosavename.get_text(), self.expnumber) plots = {'data':self.plot} - if (self.current_exp.parameters['shutter'] and - self.current_exp.parameters['sync']): + if (self.current_exp.parameters['shutter_true'] and + self.current_exp.parameters['sync_true']): plots['ft'] = self.ft_plot save.autoPlot(plots, self.autosavedir_button, @@ -944,12 +750,20 @@ class Main(object): """Activate dialogue to save current plot.""" plots = {'data':self.plot} - if (self.current_exp.parameters['shutter'] and - self.current_exp.parameters['sync']): + if (self.current_exp.parameters['shutter_true'] and + self.current_exp.parameters['sync_true']): plots['ft'] = self.ft_plot save.plotSave(plots) + def on_file_save_params_activate(self, menuitem, data=None): + """Activate dialogue to save current experiment parameters. """ + save.man_param_save(self) + + def on_file_load_params_activate(self, menuitem, data=None): + """Activate dialogue to load experiment parameters from file. """ + save.man_param_load(self) + def on_menu_dropbot_connect_activate(self, menuitem, data=None): """Listen for remote control connection from µDrop.""" self.microdrop = microdrop.microdropConnection() diff --git a/dstat_interface/parameter_test.py b/dstat_interface/parameter_test.py new file mode 100755 index 0000000000000000000000000000000000000000..4330815a3b6a2f627390649f4319b496db570372 --- /dev/null +++ b/dstat_interface/parameter_test.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# DStat Interface - An interface for the open hardware DStat potentiostat +# Copyright (C) 2014 Michael D. M. Dryden - +# Wheeler Microfluidics Laboratory +# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from errors import ErrorLogger, InputError +_logger = ErrorLogger(sender="dstat-interface-parameters") + +def lsv_test(params): + """Test LSV parameters for sanity""" + parameters = {} + for i in params: + try: + parameters[i] = int(params[i]) + except TypeError: + pass + + 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): + raise InputError(parameters['stop'], + "Stop parameter exceeds hardware limits.") + if (parameters['slope'] > 2000 or parameters['slope'] < 1): + raise InputError(parameters['slope'], + "Slope parameter exceeds hardware limits.") + if parameters['start'] == parameters['stop']: + raise InputError(parameters['start'], + "Start cannot equal Stop.") + +def cv_test(params): + """Test CV parameters for sanity""" + parameters = {} + for i in params: + try: + parameters[i] = int(params[i]) + except TypeError: + pass + + 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): + raise InputError(parameters['slope'], + "Slope parameter exceeds hardware limits.") + if (parameters['v1'] > 1499 or parameters['v1'] < -1500): + raise InputError(parameters['v1'], + "Vertex 1 parameter exceeds hardware limits.") + if (parameters['v2'] > 1499 or parameters['v2'] < -1500): + raise InputError(parameters['v2'], + "Vertex 2 parameter exceeds hardware limits.") + if (parameters['scans'] < 1 or parameters['scans'] > 255): + raise InputError(parameters['scans'], + "Scans parameter outside limits.") + if parameters['v1'] == parameters['v2']: + raise InputError(parameters['v1'], + "Vertex 1 cannot equal Vertex 2.") + +def swv_test(params): + """Test SWV parameters for sanity""" + parameters = {} + for i in params: + try: + parameters[i] = int(params[i]) + except TypeError: + pass + + if params['cyclic_true'] : + if int(params['scans']) < 1: + raise InputError(params['scans'], + "Must have at least one scan.") + else: + params['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): + raise InputError(parameters['step'], + "Step height parameter exceeds hardware limits.") + if (parameters['stop'] > 1499 or parameters['stop'] < -1500): + raise InputError(parameters['stop'], + "Stop parameter exceeds hardware limits.") + if (parameters['pulse'] > 150 or parameters['pulse'] < 1): + raise InputError(parameters['pulse'], + "Pulse height parameter exceeds hardware limits.") + if (parameters['freq'] < 1 or parameters['freq'] > 1000): + raise InputError(parameters['freq'], + "Frequency parameter outside limits.") + if parameters['start'] == parameters['stop']: + raise InputError(parameters['start'], + "Start cannot equal Stop.") + +def dpv_test(params): + """Test DPV parameters for sanity""" + parameters = {} + for i in params: + try: + parameters[i] = int(params[i]) + except TypeError: + pass + + 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): + raise InputError(parameters['step'], + "Step height parameter exceeds hardware limits.") + if (parameters['stop'] > 1499 or parameters['stop'] < -1500): + raise InputError(parameters['stop'], + "Stop parameter exceeds hardware limits.") + if (parameters['pulse'] > 150 or parameters['pulse'] < 1): + raise InputError(parameters['pulse'], + "Pulse height parameter exceeds hardware limits.") + if (parameters['period'] < 1 or parameters['period'] > 1000): + raise InputError(parameters['period'], + "Period parameter outside limits.") + if (parameters['width'] < 1 or parameters['width'] > 1000): + raise InputError(parameters['width'], + "Width parameter outside limits.") + if parameters['period'] <= parameters['width']: + raise InputError(parameters['width'], + "Width must be less than period.") + if parameters['start'] == parameters['stop']: + raise InputError(parameters['start'], + "Start cannot equal Stop.") + +def pd_test(parameters): + """Test PD parameters for sanity""" + + if (int(parameters['time']) <= 0): + raise InputError(parameters['time'], + "Time must be greater than zero.") + if (int(parameters['time']) > 65535): + raise InputError(parameters['time'], + "Time must fit in 16-bit counter.") + if (parameters['sync_true'] and parameters['shutter_true']): + if (float(parameters['sync_freq']) > 30 or + float(parameters['sync_freq']) <= 0): + raise InputError(parameters['sync_freq'], + "Frequency must be between 0 and 30 Hz.") + if (float(parameters['fft_start']) < 0 or + float(parameters['fft_start']) > float(parameters['time'])-1): + raise InputError(parameters['fft_start'], + "FFT must start between 0 and time-1.") + if float(parameters['fft_int']) < 0: + raise InputError( + parameters['fft_int'], + "Integral bandwidth must be greater than 0" + ) + +def pot_test(params): + """Test POT parameters for sanity""" + parameters = {} + for i in params: + try: + parameters[i] = int(params[i]) + except TypeError: + pass + + if (int(parameters['time']) <= 0): + raise InputError(parameters['time'], + "Time must be greater than zero.") + if (int(parameters['time']) > 65535): + raise InputError(parameters['time'], + "Time must fit in 16-bit counter.") \ No newline at end of file diff --git a/dstat_interface/params.py b/dstat_interface/params.py new file mode 100755 index 0000000000000000000000000000000000000000..565fe4b073dd13b75cbd4996bb507417d82a7d3a --- /dev/null +++ b/dstat_interface/params.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# DStat Interface - An interface for the open hardware DStat potentiostat +# Copyright (C) 2014 Michael D. M. Dryden - +# Wheeler Microfluidics Laboratory +# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import yaml + +from errors import ErrorLogger, InputError +_logger = ErrorLogger(sender="dstat-interface-params") + +def get_params(window): + """Fetches and returns dict of all parameters for saving.""" + + parameters = {} + + selection = window.exp_window.select_to_key[window.expcombobox.get_active()] + parameters['experiment_index'] = selection + + try: + parameters['version'] = window.version + except AttributeError: # Will be thrown if not connected to DStat + pass + + try: + parameters.update(window.adc_pot.params) + except InputError: + _logger.error("No gain selected.", 'INFO') + parameters.update(window.exp_window.get_params(selection)) + + return parameters + +def save_params(window, path): + """Fetches current params and saves to path.""" + + params = get_params(window) + + with open(path, 'w') as f: + yaml.dump(params, f) + +def load_params(window, path): + """Loads params from a path into UI elements.""" + + try: + get_params(window) + except InputError: # Will be thrown because no experiment will be selected + pass + + with open(path, 'r') as f: + params = yaml.load(f) + + window.adc_pot.params = params + + if not 'experiment_index' in params: + _logger.error("Missing experiment parameters.", 'WAR') + return + window.expcombobox.set_active( + window.exp_window.classes[params['experiment_index']][0]) + window.exp_window.set_params(params['experiment_index'], params) + + window.params_loaded = True \ No newline at end of file diff --git a/dstat_interface/plot.py b/dstat_interface/plot.py index 9f41ea514a7a175726ada3d17e39bfede5bf1451..d69ba65594f54d6bed1b55e5f014ed2bc05c8e09 100755 --- a/dstat_interface/plot.py +++ b/dstat_interface/plot.py @@ -173,7 +173,7 @@ class ft_box(plotbox): y = Experiment.data[1+line_number*2] x = Experiment.data[line_number*2] freq = Experiment.parameters['adc_rate_hz'] - i = search_value(x, Experiment.parameters['fft_start']) + i = search_value(x, float(Experiment.parameters['fft_start'])) y1 = y[i:] x1 = x[i:] avg = mean(y1) @@ -184,8 +184,8 @@ class ft_box(plotbox): Experiment.ft_int = integrateSpectrum( f, Y, - Experiment.parameters['sync_freq'], - Experiment.parameters['fft_int'] + float(Experiment.parameters['sync_freq']), + float(Experiment.parameters['fft_int']) ) self.axe1.lines[line_number].set_ydata(Y) self.axe1.lines[line_number].set_xdata(f)