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