+#!/usr/bin/env python
+
'''
-PROCPLOTS
-Processed plots plugin for force curves.
+procplots.py
+
+Process plots plugin for force curves.
-Licensed under the GNU GPL version 2
+Copyright ???? by ?
+with modifications by Dr. Rolf Schmidt (Concordia University, Canada)
+
+This program is released under the GNU General Public License version 2.
'''
-from libhooke import WX_GOOD
+
+import lib.libhooke as lh
import wxversion
-wxversion.select(WX_GOOD)
+wxversion.select(lh.WX_GOOD)
-import wx
-import libhookecurve as lhc
-import numpy as np
-import scipy as sp
-import scipy.signal
import copy
+from numpy import arange, diff, fft, median
+from scipy.signal import medfilt
+
+from lib.peakspot import conv_dx
+import lib.prettyformat
class procplotsCommands:
def _plug_init(self):
pass
- def do_derivplot(self,args):
+ def do_convplot(self):
'''
- DERIVPLOT
- (procplots.py plugin)
- Plots the derivate (actually, the discrete differentiation) of the current force curve
- ---------
- Syntax: derivplot
+ CONVPLOT
+ (procplots.py)
+ Plots the convolution data of the currently displayed force curve retraction.
+ ------------
+ Syntax:
+ convplot
'''
- dplot=self.derivplot_curves()
- plot_graph=self.list_of_events['plot_graph']
- wx.PostEvent(self.frame,plot_graph(plots=[dplot]))
- def derivplot_curves(self):
+ #need to convert the string that contains the list into a list
+ column = self.GetIntFromConfig('procplots', 'convplot', 'column')
+ convolution = eval(self.GetStringFromConfig('procplots', 'convplot', 'convolution'))
+ row = self.GetIntFromConfig('procplots', 'convplot', 'row')
+ whatset_str = self.GetStringFromConfig('procplots', 'convplot', 'whatset')
+ whatset = []
+ if whatset_str == 'extension':
+ whatset = [lh.EXTENSION]
+ if whatset_str == 'retraction':
+ whatset = [lh.RETRACTION]
+ if whatset_str == 'both':
+ whatset = [lh.EXTENSION, lh.RETRACTION]
+
+ #TODO: add option to keep previous derivplot
+ plot = self.GetDisplayedPlotCorrected()
+
+ for index in whatset:
+ conv_curve = copy.deepcopy(plot.curves[index])
+ #Calculate convolution
+ conv_curve.y = conv_dx(plot.curves[index].y, convolution)
+
+ conv_curve.destination.column = column
+ conv_curve.destination.row = row
+ conv_curve.title = 'Convolution'
+ plot.curves.append(conv_curve)
+
+ #warn if no flattening has been done.
+ if not self.AppliesPlotmanipulator('flatten'):
+ self.AppendToOutput('Flatten manipulator was not applied. Processing was done without flattening.')
+ self.AppendToOutput('Enable the flatten plotmanipulator for better results.')
+
+ self.UpdatePlot(plot)
+
+ def do_derivplot(self):
'''
- do_derivplot helper function
-
- create derivate plot curves for force curves.
+ Plots the discrete differentiation of the currently displayed force curve.
'''
- dplot=lhc.PlotObject()
- dplot.vectors=[]
-
- for vector in self.plots[0].vectors:
- dplot.vectors.append([])
- dplot.vectors[-1].append(vector[0][:-1])
- dplot.vectors[-1].append(np.diff(vector[1]))
-
- dplot.destination=1
- dplot.units=self.plots[0].units
-
- return dplot
+ column = self.GetIntFromConfig('procplots', 'derivplot', 'column')
+ row = self.GetIntFromConfig('procplots', 'derivplot', 'row')
+ #TODO: what os select good for?
+ select = self.GetBoolFromConfig('procplots', 'derivplot', 'select')
+ whatset_str = self.GetStringFromConfig('procplots', 'derivplot', 'whatset')
+ whatset = []
+ if whatset_str == 'extension':
+ whatset = [lh.EXTENSION]
+ if whatset_str == 'retraction':
+ whatset = [lh.RETRACTION]
+ if whatset_str == 'both':
+ whatset = [lh.EXTENSION, lh.RETRACTION]
+
+ #TODO: add option to keep previous derivplot
+ plot = self.GetDisplayedPlotCorrected()
+
+ for index in whatset:
+ deriv_curve = copy.deepcopy(plot.curves[index])
+ deriv_curve.x = deriv_curve.x[:-1]
+ deriv_curve.y = diff(deriv_curve.y).tolist()
+
+ deriv_curve.destination.column = column
+ deriv_curve.destination.row = row
+ deriv_curve.title = 'Discrete differentiation'
+ deriv_curve.units.y += ' ' + deriv_curve.units.x + '-1'
+ plot.curves.append(deriv_curve)
+
+ self.UpdatePlot(plot)
+
+ def do_replot(self):
+ '''
+ Replots the current force curve from scratch eliminating any secondary plots.
+ '''
+ self.UpdatePlot()
- def do_subtplot(self,args):
+ def do_subtplot(self):
'''
SUBTPLOT
(procplots.py plugin)
- Plots the difference between ret and ext current curve
+ Plots the difference between retraction and extension of the currently displayed curve
-------
Syntax: subtplot
'''
- #FIXME: sub_filter and sub_order must be args
- if len(self.plots[0].vectors) != 2:
- print 'This command only works on a curve with two different plots.'
- pass
+ #TODO: add option to keep previous subtplot
+ plot = self.GetDisplayedPlotCorrected()
- outplot=self.subtract_curves(sub_order=1)
+ extension = plot.curves[lh.EXTENSION]
+ retraction = plot.curves[lh.RETRACTION]
- plot_graph=self.list_of_events['plot_graph']
- wx.PostEvent(self.frame,plot_graph(plots=[outplot]))
+ extension, retraction = self.subtract_curves(extension, retraction)
- def subtract_curves(self, sub_order):
+ self.UpdatePlot(plot)
+
+ def subtract_curves(self, minuend, subtrahend):
'''
- subtracts the extension from the retraction
+ calculates: difference = minuend - subtrahend
+ (usually: extension - retraction
'''
- xext=self.plots[0].vectors[0][0]
- yext=self.plots[0].vectors[0][1]
- xret=self.plots[0].vectors[1][0]
- yret=self.plots[0].vectors[1][1]
-
- #we want the same number of points
- maxpoints_tot=min(len(xext),len(xret))
- xext=xext[0:maxpoints_tot]
- yext=yext[0:maxpoints_tot]
- xret=xret[0:maxpoints_tot]
- yret=yret[0:maxpoints_tot]
- if sub_order:
- ydiff=[yretval-yextval for yretval,yextval in zip(yret,yext)]
- else: #reverse subtraction (not sure it's useful, but...)
- ydiff=[yextval-yretval for yextval,yretval in zip(yext,yret)]
+ #we want the same number of points for minuend and subtrahend
+ #TODO: is this not already done when normalizing in the driver?
+ maxpoints_tot = min(len(minuend.x), len(subtrahend.x))
+ minuend.x = minuend.x[0:maxpoints_tot]
+ minuend.y = minuend.y[0:maxpoints_tot]
+ subtrahend.x = subtrahend.x[0:maxpoints_tot]
+ subtrahend.y = subtrahend.y[0:maxpoints_tot]
- outplot=copy.deepcopy(self.plots[0])
- outplot.vectors[0][0], outplot.vectors[1][0] = xext,xret #FIXME: if I use xret, it is not correct!
- outplot.vectors[1][1]=ydiff
- outplot.vectors[0][1]=[0 for item in outplot.vectors[1][0]]
-
- return outplot
+ subtrahend.y = [y_subtrahend - y_minuend for y_subtrahend, y_minuend in zip(subtrahend.y, minuend.y)]
+ minuend.y = [0] * len(minuend.x)
+ return minuend, subtrahend
#-----PLOT MANIPULATORS
- def plotmanip_median(self, plot, current, customvalue=None):
+ def plotmanip_median(self, plot, current, customvalue=False):
'''
does the median of the y values of a plot
'''
- if customvalue:
- median_filter=customvalue
- else:
- median_filter=self.config['medfilt']
-
- if median_filter==0:
+ median_filter = self.GetIntFromConfig('procplots', 'median')
+ if median_filter == 0:
return plot
- if float(median_filter)/2 == int(median_filter)/2:
- median_filter+=1
+ if float(median_filter) / 2 == int(median_filter) / 2:
+ median_filter += 1
- nplots=len(plot.vectors)
- c=0
- while c<nplots:
- plot.vectors[c][1]=scipy.signal.medfilt(plot.vectors[c][1],median_filter)
- c+=1
+ for curve in plot.curves:
+ curve.y = medfilt(curve.y, median_filter).tolist()
return plot
-
- def plotmanip_correct(self, plot, current, customvalue=None):
+ def plotmanip_correct(self, plot, current, customvalue=False):
'''
does the correction for the deflection for a force spectroscopy curve.
Assumes that:
- plot.vectors[1][0] is the X of retraction curve
FIXME: both this method and the picoforce driver have to be updated, deflection() must return
- a more senseful data structure!
+ a more sensible data structure!
'''
#use only for force spectroscopy experiments!
- if current.curve.experiment != 'smfs':
+ if current.driver.experiment != 'smfs':
return plot
- if customvalue != None:
- execute_me=customvalue
- else:
- execute_me=self.config['correct']
- if not execute_me:
+ if not customvalue:
+ customvalue = self.GetBoolFromConfig('procplots', 'correct')
+ if not customvalue:
return plot
- defl_ext,defl_ret=current.curve.deflection()
- #halflen=len(deflall)/2
+ defl_ext, defl_ret = current.driver.deflection()
- plot.vectors[0][0]=[(zpoint-deflpoint) for zpoint,deflpoint in zip(plot.vectors[0][0],defl_ext)]
- plot.vectors[1][0]=[(zpoint-deflpoint) for zpoint,deflpoint in zip(plot.vectors[1][0],defl_ret)]
+ plot.curves[lh.EXTENSION].x = [(zpoint - deflpoint) for zpoint,deflpoint in zip(plot.curves[lh.EXTENSION].x, defl_ext)]
+ plot.curves[lh.RETRACTION].x = [(zpoint - deflpoint) for zpoint,deflpoint in zip(plot.curves[lh.RETRACTION].x, defl_ret)]
return plot
- '''
- def plotmanip_detriggerize(self, plot, current, customvalue=None):
- #DEPRECATED
- if self.config['detrigger']==0:
+ def plotmanip_centerzero(self, plot, current, customvalue=False):
+ '''
+ Centers the force curve so the median (the free level) corresponds to 0 N
+ '''
+ #use only for force spectroscopy experiments!
+ if current.driver.experiment != 'smfs':
return plot
- cutindex=2
- startvalue=plot.vectors[0][0][0]
-
- for index in range(len(plot.vectors[0][0])-1,2,-2):
- if plot.vectors[0][0][index]>startvalue:
- cutindex=index
- else:
- break
+ if not customvalue:
+ customvalue = self.GetBoolFromConfig('procplots', 'centerzero')
+ if not customvalue:
+ return plot
- plot.vectors[0][0]=plot.vectors[0][0][:cutindex]
- plot.vectors[0][1]=plot.vectors[0][1][:cutindex]
+ levelapp = float(median(plot.curves[lh.EXTENSION].y))
+ levelret = float(median(plot.curves[lh.RETRACTION].y))
- return plot
- '''
+ level = (levelapp + levelret)/2
+ plot.curves[lh.EXTENSION].y = [i-level for i in plot.curves[lh.EXTENSION].y]
+ plot.curves[lh.RETRACTION].y = [i-level for i in plot.curves[lh.RETRACTION].y]
+ return plot
#FFT---------------------------
- def fft_plot(self, vector):
+ def fft_plot(self, curve, boundaries=[0, -1]):
'''
calculates the fast Fourier transform for the selected vector in the plot
'''
- fftplot=lhc.PlotObject()
- fftplot.vectors=[[]]
- fftlen=len(vector)/2 #need just 1/2 of length
- fftplot.vectors[-1].append(np.arange(1,fftlen).tolist())
+ fftlen = len(curve.y[boundaries[0]:boundaries[1]]) / 2 #need just 1/2 of length
+ curve.x = arange(1, fftlen).tolist()
try:
- fftplot.vectors[-1].append(abs(np.fft(vector)[1:fftlen]).tolist())
+ curve.y = abs(fft(curve.y[boundaries[0]:boundaries[1]])[1:fftlen]).tolist()
except TypeError: #we take care of newer NumPy (1.0.x)
- fftplot.vectors[-1].append(abs(np.fft.fft(vector)[1:fftlen]).tolist())
-
-
- fftplot.destination=1
+ curve.y = abs(fft.fft(curve.y[boundaries[0]:boundaries[1]])[1:fftlen]).tolist()
+ return curve
- return fftplot
-
-
- def do_fft(self,args):
+ def do_fft(self):
'''
FFT
(procplots.py plugin)
By default, fft performs the Fourier transform on all the 0-th data set on the
top plot.
- [top,bottom]: which plot is the data set to fft (default: top)
+ [top, bottom]: which plot is the data set to fft (default: top)
[select]: you pick up two points on the plot and fft only the segment between
[0,1,...]: which data set on the selected plot you want to fft (default: 0)
'''
- #args parsing
- #whatplot = plot to fft
- #whatset = set to fft in the plot
- select=('select' in args)
- if 'top' in args:
- whatplot=0
- elif 'bottom' in args:
- whatplot=1
- else:
- whatplot=0
- whatset=0
- for arg in args:
- try:
- whatset=int(arg)
- except ValueError:
- pass
+ column = self.GetIntFromConfig('procplots', 'fft', 'column')
+ row = self.GetIntFromConfig('procplots', 'fft', 'row')
+ select = self.GetBoolFromConfig('procplots', 'fft', 'select')
+ whatset_str = self.GetStringFromConfig('procplots', 'fft', 'whatset')
+ whatset = []
+ if whatset_str == 'extension':
+ whatset = [lh.EXTENSION]
+ if whatset_str == 'retraction':
+ whatset = [lh.RETRACTION]
+ if whatset_str == 'both':
+ whatset = [lh.EXTENSION, lh.RETRACTION]
if select:
- points=self._measure_N_points(N=2, whatset=whatset)
- boundaries=[points[0].index, points[1].index]
+ points = self._measure_N_points(N=2, message='Please select a region by clicking on the start and the end point.', whatset=lh.RETRACTION)
+ boundaries = [points[0].index, points[1].index]
boundaries.sort()
- y_to_fft=self.plots[whatplot].vectors[whatset][1][boundaries[0]:boundaries[1]] #y
else:
- y_to_fft=self.plots[whatplot].vectors[whatset][1] #y
-
- fftplot=self.fft_plot(y_to_fft)
- fftplot.units=['frequency', 'power']
- plot_graph=self.list_of_events['plot_graph']
- wx.PostEvent(self.frame,plot_graph(plots=[fftplot]))
+ boundaries = [0, -1]
+
+ #TODO: add option to keep previous FFT
+ plot = self.GetDisplayedPlotCorrected()
+
+ for index in whatset:
+ fft_curve = self.fft_plot(copy.deepcopy(plot.curves[index]), boundaries)
+
+ fft_curve.decimals.x = 3
+ fft_curve.decimals.y = 0
+ fft_curve.destination.column = column
+ fft_curve.destination.row = row
+ fft_curve.label = plot.curves[index].label
+ fft_curve.legend = True
+ fft_curve.prefix.x = lib.prettyformat.get_prefix(max(fft_curve.x))
+ fft_curve.prefix.y = lib.prettyformat.get_prefix(max(fft_curve.y))
+ fft_curve.title = 'FFT'
+ fft_curve.units.x = 'Hz'
+ fft_curve.units.y = 'power'
+ plot.curves.append(fft_curve)
+
+ self.UpdatePlot(plot)