diff --git a/dstat_interface/dstat_comm.py b/dstat_interface/dstat_comm.py index 56e0baa7125b6dc490798c942a462ddf4de82f73..2441081ca3a12e1116360714e8fa5ae608067490 100644 --- a/dstat_interface/dstat_comm.py +++ b/dstat_interface/dstat_comm.py @@ -608,24 +608,35 @@ class PDExp(Chronoamp): self.xmin = 0 self.xmax = self.parameters['time'] - self.commands += "E" - self.commands[2] += "R" - self.commands[2] += "1" - self.commands[2] += " " + if self.parameters['shutter']: + if self.parameters['sync']: + self.commands.append("EZ") + self.commands[-1] += str(self.parameters['sync_freq']) + self.commands[-1] += " " + else: + self.commands.append("E2") + + self.commands.append("ER1 ") if self.parameters['voltage'] == 0: # Special case where V=0 - self.commands[2] += "65535" + self.commands[-1] += "65535" else: - self.commands[2] += str(int( + self.commands[-1] += str(int( 65535-(self.parameters['voltage']*(65536./3000)))) - self.commands[2] += " " - self.commands[2] += str(self.parameters['time']) - self.commands[2] += " " + self.commands[-1] += " " + self.commands[-1] += str(self.parameters['time']) + self.commands[-1] += " " if self.parameters['interlock']: - self.commands[2] += "1" + self.commands[-1] += "1" else: - self.commands[2] += "0" - self.commands[2] += " " + self.commands[-1] += "0" + self.commands[-1] += " " + + if self.parameters['shutter']: + if self.parameters['sync']: + self.commands.append("Ez") + else: + self.commands.append("E1") class PotExp(Experiment): """Potentiometry experiment""" diff --git a/dstat_interface/interface/dstatinterface.glade b/dstat_interface/interface/dstatinterface.glade index 8f2f2726ef8b78133edb4bbbd3629fc2f649add5..0368e00ce699ee8357ca286ee9c5669e47938a36 100644 --- a/dstat_interface/interface/dstatinterface.glade +++ b/dstat_interface/interface/dstatinterface.glade @@ -1008,6 +1008,7 @@ Thanks to Christian Fobel for help with Dropbot Plugin True True + True True @@ -1074,11 +1075,81 @@ Thanks to Christian Fobel for help with Dropbot Plugin - + True - False + True + bottom - + + True + False + + + + + + + + True + False + 0.40999999642372131 + Main + + + False + + + + + True + False + + + + + + + + + 1 + + + + + True + False + FT + + + 1 + False + + + + + True + False + + + + + + + + + 2 + + + + + True + False + Periodogram + + + 2 + False + diff --git a/dstat_interface/interface/exp_int.py b/dstat_interface/interface/exp_int.py index 86e4e94a0b74b10408d30a414a7e6b3892930c96..b8a3c70234e8a22ae0724a96e43bea1109716e2c 100644 --- a/dstat_interface/interface/exp_int.py +++ b/dstat_interface/interface/exp_int.py @@ -209,8 +209,15 @@ class PD(ExpInterface): 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.buttons = map(self.builder.get_object, ['light_button', 'threshold_button']) + self.buttons = map(self.builder.get_object, + ['light_button', 'threshold_button']) + + self.shutter_buttons = map(self.builder.get_object, + ['sync_button', 'sync_freq']) def on_light_button_clicked(self, data=None): __main__.MAIN.on_pot_stop_clicked() @@ -236,6 +243,7 @@ class PD(ExpInterface): def on_threshold_button_clicked(self, data=None): __main__.MAIN.on_pot_stop_clicked() __main__.MAIN.stop_ocp() + for i in self.buttons: i.set_sensitive(False) @@ -250,6 +258,14 @@ class PD(ExpInterface): finally: gobject.timeout_add(700, restore_buttons, self.buttons) + + def on_shutter_button_toggled(self, widget): + if self.entry['shutter'].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.""" @@ -257,6 +273,9 @@ class PD(ExpInterface): 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()) return parameters diff --git a/dstat_interface/interface/pd.glade b/dstat_interface/interface/pd.glade index 05c104ee40a272331da1d1d23df88aa81414b4d1..aaa16ead58a81e2470bc40c3e2734c24a76ebe02 100644 --- a/dstat_interface/interface/pd.glade +++ b/dstat_interface/interface/pd.glade @@ -28,7 +28,7 @@ True False - 6 + 9 2 True @@ -123,6 +123,7 @@ 2 3 4 + GTK_EXPAND @@ -208,6 +209,85 @@ 6 + + + True + False + + + 2 + 6 + 7 + + + + + True + False + Electromechanical Shutter + + + 7 + 8 + + + + + Synchronous Detection (Hz) + True + True + False + 0.54000002145767212 + True + + + 8 + 9 + + + + + + True + True + + 8 + 0 + 1 + True + False + False + True + True + + + 1 + 2 + 8 + 9 + + GTK_FILL + 3 + + + + + Enable Shutter + True + True + False + True + True + + + + 1 + 2 + 7 + 8 + GTK_EXPAND + + False diff --git a/dstat_interface/main.py b/dstat_interface/main.py index 8822a4db8259731e15aaeaf8308a4cdc7046f16e..cf6f806d2fb8d276b34ecf55b43354243eeb9c00 100755 --- a/dstat_interface/main.py +++ b/dstat_interface/main.py @@ -77,6 +77,8 @@ class Main(object): self.message_context_id = self.statusbar.get_context_id("message") self.plotwindow = self.builder.get_object('plotbox') + self.ft_window = self.builder.get_object('ft_box') + self.period_window = self.builder.get_object('period_box') self.exp_window = exp_window.Experiments(self.builder) @@ -86,6 +88,8 @@ class Main(object): self.autosavename = self.builder.get_object('autosavename') self.plot = plot.plotbox(self.plotwindow) + self.ft_plot = plot.ft_box(self.ft_window) + #fill adc_pot_box self.adc_pot_box = self.builder.get_object('gain_adc_box') @@ -363,6 +367,8 @@ class Main(object): """ Starts experiment """ self.plot.clearall() self.plot.changetype(self.current_exp) + self.ft_plot.clearall() + self.ft_plot.changetype(self.current_exp) comm.serial_instance.proc_pipe_p.send(self.current_exp) @@ -404,6 +410,16 @@ class Main(object): 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) @@ -638,6 +654,11 @@ class Main(object): 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.") self.current_exp = comm.PDExp(parameters) @@ -713,10 +734,10 @@ class Main(object): try: if comm.serial_instance.data_pipe_p.poll(): incoming = comm.serial_instance.data_pipe_p.recv() - if isinstance(incoming, basestring): # Test if incoming is str - self.experiment_done() - self.on_serial_disconnect_clicked() - return False + # if isinstance(incoming, basestring): # Test if incoming is str + # self.experiment_done() + # self.on_serial_disconnect_clicked() + # return False self.line, data = incoming if self.line > self.lastdataline: @@ -729,6 +750,8 @@ class Main(object): if len(data) > 2: self.current_exp.data_extra[2*self.line+i].append( data[i+2]) + if comm.serial_instance.data_pipe_p.poll(): + self.experiment_running_data() return True return True @@ -799,6 +822,10 @@ class Main(object): gobject.source_remove(self.experiment_proc[0]) 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['sync']: + self.ft_plot.updateline(self.current_exp, 0) + self.ft_plot.redraw() self.databuffer.set_text("") self.databuffer.place_cursor(self.databuffer.get_start_iter()) diff --git a/dstat_interface/plot.py b/dstat_interface/plot.py index 4002efc36fb30dbd480711a1f62b0a2886b83085..8b286fa497b9017587138632ef0998388fec7ea8 100644 --- a/dstat_interface/plot.py +++ b/dstat_interface/plot.py @@ -31,6 +31,27 @@ from matplotlib.backends.backend_gtkagg \ import FigureCanvasGTKAgg as FigureCanvas from matplotlib.backends.backend_gtkagg \ import NavigationToolbar2GTKAgg as NavigationToolbar + +from numpy import sin, linspace, pi, mean +from scipy import fft, arange +from scipy.signal import blackman + +def plotSpectrum(y,Fs): + """ + Plots a Single-Sided Amplitude Spectrum of y(t) + """ + y = y-mean(y) + n = len(y) # length of the signal + k = arange(n) + T = n/Fs + frq = k/T # two sides frequency range + frq = frq[range(n/2)] # one side frequency range + W = blackman(n) + Y = fft(y*W)/n # fft computing and normalization + Y = abs(Y[range(n/2)]) + + + return (frq, Y) class plotbox(object): """Contains main data plot and associated methods.""" @@ -104,4 +125,21 @@ class plotbox(object): self.figure.canvas.draw() return True + +class ft_box(plotbox): + def updateline(self, Experiment, line_number): + x, y = plotSpectrum(Experiment.data[1+line_number*2], Experiment.parameters['adc_rate_hz']) + self.lines[line_number].set_ydata(y) + self.lines[line_number].set_xdata(x) + + def changetype(self, Experiment): + """Change plot type. Set axis labels and x bounds to those stored + in the Experiment instance. + """ + self.axe1.set_xlabel("Freq (Hz)") + self.axe1.set_ylabel("|Y| (A/Hz)") + self.axe1.set_xlim(0, Experiment.parameters['adc_rate_hz']/2) + + self.figure.canvas.draw() +