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