Added illysam branch
[hooke.git] / plugins / procplots.py
1 #!/usr/bin/env python
2
3 '''
4 procplots.py
5
6 Process plots plugin for force curves.
7
8 Copyright ???? by ?
9 with modifications by Dr. Rolf Schmidt (Concordia University, Canada)
10
11 This program is released under the GNU General Public License version 2.
12 '''
13
14 import lib.libhooke as lh
15 import wxversion
16 wxversion.select(lh.WX_GOOD)
17
18 import copy
19 from numpy import arange, diff, fft
20 from scipy.signal import medfilt
21
22 from lib.peakspot import conv_dx
23
24 class procplotsCommands:
25
26     def _plug_init(self):
27         pass
28
29     def do_convplot(self):
30         '''
31         CONVPLOT
32         (procplots.py)
33         Plots the convolution data of the currently displayed force curve retraction.
34         ------------
35         Syntax:
36         convplot
37         '''
38
39         #need to convert the string that contains the list into a list
40         column = self.GetIntFromConfig('procplots', 'convplot', 'column')
41         convolution = eval(self.GetStringFromConfig('procplots', 'convplot', 'convolution'))
42         row = self.GetIntFromConfig('procplots', 'convplot', 'row')
43         whatset_str = self.GetStringFromConfig('procplots', 'convplot', 'whatset')
44         whatset = []
45         if whatset_str == 'extension':
46             whatset = [lh.EXTENSION]
47         if whatset_str == 'retraction':
48             whatset = [lh.RETRACTION]
49         if whatset_str == 'both':
50             whatset = [lh.EXTENSION, lh.RETRACTION]
51
52         #TODO: add option to keep previous derivplot
53         plot = self.GetDisplayedPlotCorrected()
54
55         for index in whatset:
56             conv_curve = copy.deepcopy(plot.curves[index])
57             #Calculate convolution
58             conv_curve.y = conv_dx(plot.curves[index].y, convolution)
59
60             conv_curve.destination.column = column
61             conv_curve.destination.row = row
62             conv_curve.title = 'Convolution'
63             plot.curves.append(conv_curve)
64
65         #warn if no flattening has been done.
66         if not self.AppliesPlotmanipulator('flatten'):
67             self.AppendToOutput('Flatten manipulator was not applied. Processing was done without flattening.')
68             self.AppendToOutput('Enable the flatten plotmanipulator for better results.')
69
70         self.UpdatePlot(plot)
71
72
73     def do_derivplot(self):
74         '''
75         DERIVPLOT
76         (procplots.py plugin)
77         Plots the discrete differentiation of the currently displayed force curve retraction
78         ---------
79         Syntax: derivplot
80         '''
81         column = self.GetIntFromConfig('procplots', 'derivplot', 'column')
82         row = self.GetIntFromConfig('procplots', 'derivplot', 'row')
83         select = self.GetBoolFromConfig('procplots', 'derivplot', 'select')
84         whatset_str = self.GetStringFromConfig('procplots', 'derivplot', 'whatset')
85         whatset = []
86         if whatset_str == 'extension':
87             whatset = [lh.EXTENSION]
88         if whatset_str == 'retraction':
89             whatset = [lh.RETRACTION]
90         if whatset_str == 'both':
91             whatset = [lh.EXTENSION, lh.RETRACTION]
92
93         #TODO: add option to keep previous derivplot
94         plot = self.GetDisplayedPlotCorrected()
95
96         for index in whatset:
97             deriv_curve = copy.deepcopy(plot.curves[index])
98             deriv_curve.x = deriv_curve.x[:-1]
99             deriv_curve.y = diff(deriv_curve.y).tolist()
100
101             deriv_curve.destination.column = column
102             deriv_curve.destination.row = row
103             deriv_curve.title = 'Discrete differentiation'
104             deriv_curve.units.y += ' ' + deriv_curve.units.x + '-1'
105             plot.curves.append(deriv_curve)
106
107         self.UpdatePlot(plot)
108
109     def do_subtplot(self):
110         '''
111         SUBTPLOT
112         (procplots.py plugin)
113         Plots the difference between retraction and extension of the currently displayed curve
114         -------
115         Syntax: subtplot
116         '''
117         #TODO: what is sub_filter supposed to do?
118
119         #TODO: add option to keep previous subtplot
120         plot = self.GetDisplayedPlotCorrected()
121
122         extension = plot.curves[lh.EXTENSION]
123         retraction = plot.curves[lh.RETRACTION]
124
125         extension, retraction = self.subtract_curves(extension, retraction)
126
127         self.UpdatePlot(plot)
128
129     def subtract_curves(self, minuend, subtrahend):
130         '''
131         calculates: difference = minuend - subtrahend
132         (usually:              extension - retraction
133         '''
134
135         #we want the same number of points for minuend and subtrahend
136         #TODO: is this not already done when normalizing in the driver?
137         maxpoints_tot = min(len(minuend.x), len(subtrahend.x))
138         minuend.x = minuend.x[0:maxpoints_tot]
139         minuend.y = minuend.y[0:maxpoints_tot]
140         subtrahend.x = subtrahend.x[0:maxpoints_tot]
141         subtrahend.y = subtrahend.y[0:maxpoints_tot]
142
143         subtrahend.y = [y_subtrahend - y_minuend for y_subtrahend, y_minuend in zip(subtrahend.y, minuend.y)]
144         minuend.y = [0] * len(minuend.x)
145
146         return minuend, subtrahend
147
148 #-----PLOT MANIPULATORS
149     def plotmanip_median(self, plot, current, customvalue=False):
150         '''
151         does the median of the y values of a plot
152         '''
153         median_filter = self.GetIntFromConfig('procplots', 'median')
154         if median_filter == 0:
155             return plot
156
157         if float(median_filter) / 2 == int(median_filter) / 2:
158             median_filter += 1
159
160         for curve in plot.curves:
161             curve.y = medfilt(curve.y, median_filter).tolist()
162
163         return plot
164
165     def plotmanip_correct(self, plot, current, customvalue=False):
166         '''
167         does the correction for the deflection for a force spectroscopy curve.
168         Assumes that:
169         - the current plot has a deflection() method that returns a vector of values
170         - the deflection() vector is as long as the X of extension + the X of retraction
171         - plot.vectors[0][0] is the X of extension curve
172         - plot.vectors[1][0] is the X of retraction curve
173
174         FIXME: both this method and the picoforce driver have to be updated, deflection() must return
175         a more sensible data structure!
176         '''
177         #use only for force spectroscopy experiments!
178         if current.driver.experiment != 'smfs':
179             return plot
180
181         if not customvalue:
182             customvalue = self.GetBoolFromConfig('procplots', 'correct')
183         if not customvalue:
184             return plot
185
186         defl_ext, defl_ret = current.driver.deflection()
187
188         plot.curves[lh.EXTENSION].x = [(zpoint - deflpoint) for zpoint,deflpoint in zip(plot.curves[lh.EXTENSION].x, defl_ext)]
189         plot.curves[lh.RETRACTION].x = [(zpoint - deflpoint) for zpoint,deflpoint in zip(plot.curves[lh.RETRACTION].x, defl_ret)]
190
191         return plot
192
193 #FFT---------------------------
194     def fft_plot(self, curve, boundaries=[0, -1]):
195         '''
196         calculates the fast Fourier transform for the selected vector in the plot
197         '''
198
199         fftlen = len(curve.y[boundaries[0]:boundaries[1]]) / 2 #need just 1/2 of length
200         curve.x = arange(1, fftlen).tolist()
201
202         try:
203             curve.y = abs(fft(curve.y[boundaries[0]:boundaries[1]])[1:fftlen]).tolist()
204         except TypeError: #we take care of newer NumPy (1.0.x)
205             curve.y = abs(fft.fft(curve.y[boundaries[0]:boundaries[1]])[1:fftlen]).tolist()
206
207         return curve
208
209     def do_fft(self):
210         '''
211         FFT
212         (procplots.py plugin)
213         Plots the fast Fourier transform of the selected plot
214         ---
215         Syntax: fft [top,bottom] [select] [0,1...]
216
217         By default, fft performs the Fourier transform on all the 0-th data set on the
218         top plot.
219
220         [top, bottom]: which plot is the data set to fft (default: top)
221         [select]: you pick up two points on the plot and fft only the segment between
222         [0,1,...]: which data set on the selected plot you want to fft (default: 0)
223         '''
224
225         column = self.GetIntFromConfig('procplots', 'fft', 'column')
226         row = self.GetIntFromConfig('procplots', 'fft', 'row')
227         select = self.GetBoolFromConfig('procplots', 'fft', 'select')
228         whatset_str = self.GetStringFromConfig('procplots', 'fft', 'whatset')
229         whatset = []
230         if whatset_str == 'extension':
231             whatset = [lh.EXTENSION]
232         if whatset_str == 'retraction':
233             whatset = [lh.RETRACTION]
234         if whatset_str == 'both':
235             whatset = [lh.EXTENSION, lh.RETRACTION]
236
237         if select:
238             points = self._measure_N_points(N=2, message='Please select a region by clicking on the start and the end point.', whatset=1)
239             boundaries = [points[0].index, points[1].index]
240             boundaries.sort()
241         else:
242             boundaries = [0, -1]
243
244         #TODO: add option to keep previous FFT
245         plot = self.GetDisplayedPlotCorrected()
246
247         for index in whatset:
248             fft_curve = self.fft_plot(copy.deepcopy(plot.curves[index]), boundaries)
249
250             fft_curve.destination.column = column
251             fft_curve.destination.row = row
252             fft_curve.title = 'FFT'
253             fft_curve.units.x = 'frequency'
254             fft_curve.units.y = 'power'
255             plot.curves.append(fft_curve)
256
257         self.UpdatePlot(plot)