b7826aed227cc2499adf0778c9365ddb6db1c24f
[hooke.git] / hooke / plugin / procplots.py
1 '''
2 PROCPLOTS
3 Processed plots plugin for force curves.
4
5 Licensed under the GNU GPL version 2
6 '''
7 from ..libhooke import WX_GOOD
8 import wxversion
9 wxversion.select(WX_GOOD)
10
11 import wx
12 import numpy as np
13 import scipy as sp
14 import scipy.signal
15 import copy
16
17 from .. import libhookecurve as lhc
18
19
20 class procplotsCommands(object):
21
22     def _plug_init(self):
23         pass
24
25     def do_derivplot(self,args):
26         '''
27         DERIVPLOT
28         (procplots.py plugin)
29         Plots the derivate (actually, the discrete differentiation) of the current force curve
30         ---------
31         Syntax: derivplot
32         '''
33         dplot=self.derivplot_curves()
34         plot_graph=self.list_of_events['plot_graph']
35         wx.PostEvent(self.frame,plot_graph(plots=[dplot]))
36
37     def derivplot_curves(self):
38         '''
39         do_derivplot helper function
40
41         create derivate plot curves for force curves.
42         '''
43         dplot=lhc.PlotObject()
44         dplot.vectors=[]
45
46         for vector in self.plots[0].vectors:
47             dplot.vectors.append([])
48             dplot.vectors[-1].append(vector[0][:-1])
49             dplot.vectors[-1].append(np.diff(vector[1]))
50
51         dplot.destination=1
52         dplot.units=self.plots[0].units
53
54         return dplot
55
56     def do_subtplot(self,args):
57         '''
58         SUBTPLOT
59         (procplots.py plugin)
60         Plots the difference between ret and ext current curve
61         -------
62         Syntax: subtplot
63         '''
64         #FIXME: sub_filter and sub_order must be args
65
66         if len(self.plots[0].vectors) != 2:
67             print 'This command only works on a curve with two different plots.'
68             pass
69
70         outplot=self.subtract_curves(sub_order=1)
71
72         plot_graph=self.list_of_events['plot_graph']
73         wx.PostEvent(self.frame,plot_graph(plots=[outplot]))
74
75     def subtract_curves(self, sub_order):
76         '''
77         subtracts the extension from the retraction
78         '''
79         xext=self.plots[0].vectors[0][0]
80         yext=self.plots[0].vectors[0][1]
81         xret=self.plots[0].vectors[1][0]
82         yret=self.plots[0].vectors[1][1]
83
84         #we want the same number of points
85         maxpoints_tot=min(len(xext),len(xret))
86         xext=xext[0:maxpoints_tot]
87         yext=yext[0:maxpoints_tot]
88         xret=xret[0:maxpoints_tot]
89         yret=yret[0:maxpoints_tot]
90
91         if sub_order:
92             ydiff=[yretval-yextval for yretval,yextval in zip(yret,yext)]
93         else: #reverse subtraction (not sure it's useful, but...)
94             ydiff=[yextval-yretval for yextval,yretval in zip(yext,yret)]
95
96         outplot=copy.deepcopy(self.plots[0])
97         outplot.vectors[0][0], outplot.vectors[1][0] = xext,xret #FIXME: if I use xret, it is not correct!
98         outplot.vectors[1][1]=ydiff
99         outplot.vectors[0][1]=[0 for item in outplot.vectors[1][0]]
100
101         return outplot
102
103
104 #-----PLOT MANIPULATORS
105     def plotmanip_median(self, plot, current, customvalue=None):
106         '''
107         does the median of the y values of a plot
108         '''
109         if customvalue:
110             median_filter=customvalue
111         else:
112             median_filter=self.config['medfilt']
113
114         if median_filter==0:
115             return plot
116
117         if float(median_filter)/2 == int(median_filter)/2:
118             median_filter+=1
119
120         nplots=len(plot.vectors)
121         c=0
122         while c<nplots:
123             plot.vectors[c][1]=scipy.signal.medfilt(plot.vectors[c][1],median_filter)
124             c+=1
125
126         return plot
127
128
129     def plotmanip_correct(self, plot, current, customvalue=None):
130         '''
131         does the correction for the deflection for a force spectroscopy curve.
132         Assumes that:
133         - the current plot has a deflection() method that returns a vector of values
134         - the deflection() vector is as long as the X of extension + the X of retraction
135         - plot.vectors[0][0] is the X of extension curve
136         - plot.vectors[1][0] is the X of retraction curve
137
138         FIXME: both this method and the picoforce driver have to be updated, deflection() must return
139         a more senseful data structure!
140         '''
141         #use only for force spectroscopy experiments!
142         if current.curve.experiment != 'smfs':
143             return plot
144
145         if customvalue != None:
146             execute_me=customvalue
147         else:
148             execute_me=self.config['correct']
149         if not execute_me:
150             return plot
151
152         defl_ext,defl_ret=current.curve.deflection()
153         #halflen=len(deflall)/2
154
155         plot.vectors[0][0]=[(zpoint-deflpoint) for zpoint,deflpoint in zip(plot.vectors[0][0],defl_ext)]
156         plot.vectors[1][0]=[(zpoint-deflpoint) for zpoint,deflpoint in zip(plot.vectors[1][0],defl_ret)]
157
158         return plot
159
160
161     def plotmanip_centerzero(self, plot, current, customvalue=None):
162         '''
163         Centers the force curve so the median (the free level) corresponds to 0 N
164         Assumes that:
165         - plot.vectors[0][1] is the Y of extension curve
166         - plot.vectors[1][1] is the Y of retraction curve
167         
168        
169         '''
170         #use only for force spectroscopy experiments!
171         if current.curve.experiment != 'smfs':
172             return plot
173     
174         if customvalue != None:
175             execute_me=customvalue
176         else:
177             execute_me=self.config['centerzero']
178         if not execute_me:
179             return plot
180      
181         
182         
183         #levelapp=float(np.median(plot.vectors[0][1]))
184         levelret=float(np.median(plot.vectors[1][1][-300:-1]))
185
186         level=levelret  
187
188         approach=[i-level for i in plot.vectors[0][1]]
189         retract=[i-level for i in plot.vectors[1][1]]
190         
191         plot.vectors[0][1]=approach     
192         plot.vectors[1][1]=retract      
193         return plot
194     
195     '''
196     def plotmanip_detriggerize(self, plot, current, customvalue=None):
197         #DEPRECATED
198         if self.config['detrigger']==0:
199             return plot
200
201         cutindex=2
202         startvalue=plot.vectors[0][0][0]
203
204         for index in range(len(plot.vectors[0][0])-1,2,-2):
205            if plot.vectors[0][0][index]>startvalue:
206                 cutindex=index
207            else:
208                 break
209
210         plot.vectors[0][0]=plot.vectors[0][0][:cutindex]
211         plot.vectors[0][1]=plot.vectors[0][1][:cutindex]
212
213         return plot
214     '''
215
216
217
218 #FFT---------------------------
219     def fft_plot(self, vector):
220         '''
221         calculates the fast Fourier transform for the selected vector in the plot
222         '''
223         fftplot=lhc.PlotObject()
224         fftplot.vectors=[[]]
225
226         fftlen=len(vector)/2 #need just 1/2 of length
227         fftplot.vectors[-1].append(np.arange(1,fftlen).tolist())
228
229         try:
230             fftplot.vectors[-1].append(abs(np.fft(vector)[1:fftlen]).tolist())
231         except TypeError: #we take care of newer NumPy (1.0.x)
232             fftplot.vectors[-1].append(abs(np.fft.fft(vector)[1:fftlen]).tolist())
233
234
235         fftplot.destination=1
236
237
238         return fftplot
239
240
241     def do_fft(self,args):
242         '''
243         FFT
244         (procplots.py plugin)
245         Plots the fast Fourier transform of the selected plot
246         ---
247         Syntax: fft [top,bottom] [select] [0,1...]
248
249         By default, fft performs the Fourier transform on all the 0-th data set on the
250         top plot.
251
252         [top,bottom]: which plot is the data set to fft (default: top)
253         [select]: you pick up two points on the plot and fft only the segment between
254         [0,1,...]: which data set on the selected plot you want to fft (default: 0)
255         '''
256
257         #args parsing
258         #whatplot = plot to fft
259         #whatset = set to fft in the plot
260         select=('select' in args)
261         if 'top' in args:
262             whatplot=0
263         elif 'bottom' in args:
264             whatplot=1
265         else:
266             whatplot=0
267         whatset=0
268         for arg in args:
269             try:
270                 whatset=int(arg)
271             except ValueError:
272                 pass
273
274         if select:
275             points=self._measure_N_points(N=2, whatset=whatset)
276             boundaries=[points[0].index, points[1].index]
277             boundaries.sort()
278             y_to_fft=self.plots[whatplot].vectors[whatset][1][boundaries[0]:boundaries[1]] #y
279         else:
280             y_to_fft=self.plots[whatplot].vectors[whatset][1] #y
281
282         fftplot=self.fft_plot(y_to_fft)
283         fftplot.units=['frequency', 'power']
284         plot_graph=self.list_of_events['plot_graph']
285         wx.PostEvent(self.frame,plot_graph(plots=[fftplot]))