3 # calibcant - tools for thermally calibrating AFM cantilevers
5 # Copyright (C) 2008-2010 W. Trevor King <wking@drexel.edu>
7 # This file is part of CalibCant.
9 # CalibCant is free software: you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation, either
12 # version 3 of the License, or (at your option) any later version.
14 # CalibCant is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU Lesser General Public License for more details.
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with CalibCant. If not, see
21 # <http://www.gnu.org/licenses/>.
24 Aquire, save, and load cantilever calibration bump data.
25 For measuring photodiode sensitivity.
27 W. Trevor King Dec. 2007 - Oct. 2008
29 The relevent physical quantities are :
30 Vzp_out Output z-piezo voltage (what we generate)
31 Vzp Applied z-piezo voltage (after external ZPGAIN)
32 Zp The z-piezo position
33 Zcant The cantilever vertical deflection
34 Vphoto The photodiode vertical deflection voltage (what we measure)
36 Which are related by the parameters :
38 zpSensitivity Zp / Vzp
39 photoSensitivity Vphoto / Zcant
41 Cantilever calibration assumes a pre-calibrated z-piezo (zpSensitivity) and
42 amplifier (zpGain). In our lab, the z-piezo is calibrated by imaging a
43 calibration sample, which has features with well defined sizes, and the gain
44 is set with a knob on the Nanoscope.
46 photoSensitivity is measured by bumping the cantilever against the surface,
47 where Zp = Zcant (see the bump_*() family of functions)
48 The measured slope Vphoto/Vout is converted to photoSensitivity via
49 Vphoto/Vzp_out * Vzp_out/Vzp * Vzp/Zp * Zp/Zcant = Vphoto/Zcant
50 (measured) (1/zpGain) (1/zpSensitivity) (1) (photoSensitivity)
52 We do all these measurements a few times to estimate statistical errors.
54 The functions are layed out in the families:
56 For each family, * can be any of :
57 aquire get real-world data
58 save store real-world data to disk
59 load get real-world data from disk
60 analyze interperate the real-world data.
61 plot show a nice graphic to convince people we're working :p
63 read a file with a list of paths to previously saved real world data
64 load each file using *_load(), analyze using *_analyze(), and
65 optionally plot using *_plot().
66 Intended for re-processing old data.
67 A family name without any _* extension (e.g. bump()),
68 runs *_aquire(), *_save(), *_analyze(), *_plot().
76 import piezo.z_piezo_utils as z_piezo_utils
78 from .bump_analyze import bump_analyze
81 LOG_DATA = True # quietly grab all real-world data and log to LOG_DIR
82 LOG_DIR = '$DEFAULT$/calibrate_cantilever'
84 TEXT_VERBOSE = True # for debugging
89 def bump_aquire(zpiezo, push_depth, npoints, freq) :
91 Ramps closer push_depth and returns to the original position.
93 zpiezo an opened zpiezo.zpiezo instance
94 push_depth distance to approach, in nm
95 npoints number points during the approach and during the retreat
96 freq rate at which data is aquired
97 log_dir directory to log data to (see data_logger.py).
98 None to turn off logging (see also the global LOG_DATA).
99 Returns the aquired ramp data dictionary, with data in DAC/ADC bits.
101 # generate the bump output
102 start_pos = zpiezo.curPos()
103 pos_dist = zpiezo.pos_nm2out(push_depth) - zpiezo.pos_nm2out(0)
104 close_pos = start_pos + pos_dist
105 appr = linspace(start_pos, close_pos, npoints)
106 retr = linspace(close_pos, start_pos, npoints)
107 out = concatenate((appr, retr))
108 # run the bump, and measure deflection
110 print "Bump %g nm" % push_depth
111 data = zpiezo.ramp(out, freq)
112 # default saving, so we have a log in-case the operator is lazy ;)
113 if LOG_DATA == True :
114 log = data_logger.data_log(LOG_DIR, noclobber_logsubdir=False,
115 log_name="bump_surface")
116 log.write_dict_of_arrays(data)
119 def bump_save(data, log_dir) :
120 "Save the dictionary data, using data_logger.data_log()"
122 log = data_logger.data_log(log_dir, noclobber_logsubdir=False,
124 log.write_dict_of_arrays(data)
126 def bump_load(datafile) :
127 "Load the dictionary data, using data_logger.date_load()"
128 dl = data_logger.data_load()
129 data = dl.read_dict_of_arrays(path)
132 def bump_plot(data, plotVerbose) :
133 "Plot the bump (Vphoto vs Vzp) if plotVerbose or PYLAB_VERBOSE == True"
134 if plotVerbose or PYLAB_VERBOSE :
136 _pylab.figure(BASE_FIGNUM)
137 _pylab.plot(data["Z piezo output"], data["Deflection input"],
139 _pylab.title("bump surface")
140 _pylab.legend(loc='upper left')
143 def bump(zpiezo, push_depth, npoints=1024, freq=100e3,
147 Wrapper around bump_aquire(), bump_save(), bump_analyze(), bump_plot()
149 data = bump_aquire(zpiezo, push_depth, npoints, freq)
150 bump_save(data, log_dir)
151 photoSensitivity = bump_analyze(data, zpiezo.gain, zpiezo.sensitivity,
152 zpiezo.pos_out2V, zpiezo.def_in2V)
153 bump_plot(data, plotVerbose)
154 return photoSensitivity
156 def bump_load_analyze_tweaked(tweak_file, zpGain=_usual_zpGain,
157 zpSensitivity=_usual_zpSensitivity,
158 Vzp_out2V=_usual_Vzp_out2V,
159 Vphoto_in2V=_usual_Vphoto_in2V,
161 "Load the output file of tweak_calib_bump.sh, return an array of slopes"
162 photoSensitivity = []
163 for line in file(tweak_file, 'r') :
164 parsed = line.split()
165 path = parsed[0].split('\n')[0]
167 full_data = bump_load(path)
168 if len(parsed) == 1 :
169 data = full_data # use whole bump
171 # use the listed sections
174 for rng in parsed[1:] :
178 zp.extend(full_data['Z piezo output'][starti:stopi])
179 df.extend(full_data['Deflection input'][starti:stopi])
180 data = {'Z piezo output': array(zp),
181 'Deflection input':array(df)}
182 pSi = bump_analyze(data, zpGain, zpSensitivity,
183 Vzp_out2V, Vphoto_in2V, plotVerbose)
184 photoSensitivity.append(pSi)
185 bump_plot(data, plotVervose)
186 return array(photoSensitivity, dtype=numpy.float)