Run update-copyright.py
[calibcant.git] / bin / calibcant-calibrate.py
1 #!/usr/bin/env python
2 # calibcant - tools for thermally calibrating AFM cantilevers
3 #
4 # Copyright (C) 2012-2013 W. Trevor King <wking@tremily.us>
5 #
6 # This file is part of calibcant.
7 #
8 # calibcant is free software: you can redistribute it and/or modify it under
9 # the terms of the GNU General Public License as published by the Free Software
10 # Foundation, either version 3 of the License, or (at your option) any later
11 # version.
12 #
13 # calibcant is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # calibcant.  If not, see <http://www.gnu.org/licenses/>.
19
20 """Run a cantilever calibration using the default AFM
21 (``pyafm.storage.load_afm()``).
22 """
23
24 import argparse as _argparse
25 import time as _time
26
27 import h5py as _h5py
28 from pyafm.storage import load_afm as _load_afm
29
30 from calibcant.calibrate import Calibrator as _Calibrator
31 import calibcant.config as _config
32
33
34 _module_doc = __doc__
35
36 def main(args):
37     parser = _argparse.ArgumentParser(description=_module_doc)
38     parser.add_argument(
39         '--num-bumps', type=int,
40         help='Number of surface bumps')
41     parser.add_argument(
42         '--num-temperatures', type=int,
43         help='Number of temperature measurements')
44     parser.add_argument(
45         '--num-vibrations', type=int,
46         help='Number of thermal vibration measurements')
47
48     args = parser.parse_args(args)
49
50     timestamp = '{0}-{1:02d}-{2:02d}T{3:02d}-{4:02d}-{5:02d}'.format(
51         *_time.localtime())
52     filename = '{}-calibcant-data.h5'.format(timestamp)
53     config = _config.CalibrateConfig()
54     config['bump'] = _config.BumpConfig()
55     config['bump'].update(
56         {'model':_config.Linear, 'initial-position':-150e-9})
57     config['temperature'] = _config.TemperatureConfig()
58     config['vibration'] = _config.VibrationConfig()
59     if args.num_bumps is None:
60         args.num_bumps = config['num-bumps']
61     else:
62         config['num-bumps'] = args.num_bumps
63     if args.num_temperatures is None:
64         args.num_temperatures = config['num-temperatures']
65     else:
66         config['num-temperatures'] = args.num_temperatures
67     if args.num_vibrations is None:
68         args.num_vibrations = config['num-vibrations']
69     else:
70         config['num-vibrations'] = args.num_vibrations
71     insufficient_calibration_data = 0 in [
72         args.num_bumps, args.num_temperatures, args.num_vibrations]
73     devices = []
74     try:
75         afm = _load_afm()
76         afm.load_from_config(devices=devices)
77         calibrator = _Calibrator(config=config, afm=afm)
78         calibrator.setup_config()
79         deflection = afm.piezo.read_deflection()
80         try:
81             position,deflection = afm.stepper_approach(
82                 target_deflection=deflection + 1e3, record_data=True)
83             with _h5py.File(filename) as f:
84                 f['/approach/position'] = position
85                 f['/approach/deflection'] = deflection
86             if insufficient_calibration_data:
87                 data = calibrator.acquire(filename=filename)
88             else:
89                 k,k_s,data = calibrator.calibrate(filename=filename)
90         except:
91             afm.move_away_from_surface()
92             afm.piezo.zero()
93             raise
94     finally:
95         for device in devices:
96             device.close()
97     if insufficient_calibration_data:
98         for count,field,label in [
99             (args.num_bumps, 'bump', 'photodiode sensitivity (V/m)'),
100             (args.num_temperatures, 'temperature', 'temperature (K)'),
101             (args.num_vibrations, 'vibration', 'variance (V**2)')]:
102             if count:
103                 d = data[field]
104                 print('{}: {:g} +/- {:g}'.format(label, d.mean(), d.std()))
105     else:
106         print('k: {:g} +/- {:g}'.format(k, k_s))
107     return 0
108
109 if __name__ == '__main__':
110     import sys
111
112     sys.exit(main(sys.argv[1:]))