calibcant-calibrate.py: Record the stepper approach trace
[calibcant.git] / bin / calibcant-calibrate.py
1 #!/usr/bin/env python
2 # calibcant - tools for thermally calibrating AFM cantilevers
3 #
4 # Copyright
5
6 """Run a cantilever calibration using the default AFM
7 (``pyafm.storage.load_afm()``).
8 """
9
10 import argparse as _argparse
11 import time as _time
12
13 import h5py as _h5py
14 from pyafm.storage import load_afm as _load_afm
15
16 from calibcant.calibrate import Calibrator as _Calibrator
17 import calibcant.config as _config
18
19
20 _module_doc = __doc__
21
22 def main(args):
23     parser = _argparse.ArgumentParser(description=_module_doc)
24     parser.add_argument(
25         '--num-bumps', type=int,
26         help='Number of surface bumps')
27     parser.add_argument(
28         '--num-temperatures', type=int,
29         help='Number of temperature measurements')
30     parser.add_argument(
31         '--num-vibrations', type=int,
32         help='Number of thermal vibration measurements')
33
34     args = parser.parse_args(args)
35
36     timestamp = '{0}-{1:02d}-{2:02d}T{3:02d}-{4:02d}-{5:02d}'.format(
37         *_time.localtime())
38     filename = '{}-calibcant-data.h5'.format(timestamp)
39     config = _config.CalibrateConfig()
40     config['bump'] = _config.BumpConfig()
41     config['bump'].update(
42         {'model':_config.Linear, 'initial-position':-150e-9})
43     config['temperature'] = _config.TemperatureConfig()
44     config['vibration'] = _config.VibrationConfig()
45     if args.num_bumps is None:
46         args.num_bumps = config['num-bumps']
47     else:
48         config['num-bumps'] = args.num_bumps
49     if args.num_temperatures is None:
50         args.num_temperatures = config['num-temperatures']
51     else:
52         config['num-temperatures'] = args.num_temperatures
53     if args.num_vibrations is None:
54         args.num_vibrations = config['num-vibrations']
55     else:
56         config['num-vibrations'] = args.num_vibrations
57     insufficient_calibration_data = 0 in [
58         args.num_bumps, args.num_temperatures, args.num_vibrations]
59     devices = []
60     try:
61         afm = _load_afm()
62         afm.load_from_config(devices=devices)
63         calibrator = _Calibrator(config=config, afm=afm)
64         calibrator.setup_config()
65         deflection = afm.piezo.read_deflection()
66         try:
67             position,deflection = afm.stepper_approach(
68                 target_deflection=deflection + 1e3, record_data=True)
69             with _h5py.File(filename) as f:
70                 f['/approach/position'] = position
71                 f['/approach/deflection'] = deflection
72             if insufficient_calibration_data:
73                 data = calibrator.acquire(filename=filename)
74             else:
75                 k,k_s,data = calibrator.calibrate(filename=filename)
76         except:
77             afm.move_away_from_surface()
78             afm.piezo.zero()
79             raise
80     finally:
81         for device in devices:
82             device.close()
83     if insufficient_calibration_data:
84         for count,field,label in [
85             (args.num_bumps, 'bump', 'photodiode sensitivity (V/m)'),
86             (args.num_temperatures, 'temperature', 'temperature (K)'),
87             (args.num_vibrations, 'vibration', 'variance (V**2)')]:
88             if count:
89                 d = data[field]
90                 print('{}: {:g} +/- {:g}'.format(label, d.mean(), d.std()))
91     else:
92         print('k: {:g} +/- {:g}'.format(k, k_s))
93     return 0
94
95 if __name__ == '__main__':
96     import sys
97
98     sys.exit(main(sys.argv[1:]))