WLC and FJC output are now with 2 decimal precision. Added a comment on autopeak...
[hooke.git] / procplots.py
1 #!/usr/bin/env python
2
3 '''
4 PROCPLOTS
5 Processed plots plugin for force curves.
6
7 Licensed under the GNU GPL version 2
8 '''
9 from libhooke import WX_GOOD
10 import wxversion
11 wxversion.select(WX_GOOD)
12
13 import wx
14 import libhookecurve as lhc
15 import numpy as np
16 import scipy as sp
17 import scipy.signal
18 import copy
19
20 class procplotsCommands:
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]))
286