1 # Copyright (C) 2011-2012 W. Trevor King <wking@drexel.edu>
3 # This file is part of pypiezo.
5 # pypiezo is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
10 # pypiezo is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # pypiezo. If not, see <http://www.gnu.org/licenses/>.
17 """Utilities for wiggling a the piezo near the surface.
19 This helps you detect interference between the laser bouncing off the
20 tip and the surface. The wiggling continues until you stop it, which
21 gives you feedback while you reposition the laser to minimize the
22 interference. One day we'll all have fancy AFMs with software control
23 over the laser alignment, but until then, we've got this module
24 helping you with your thumbscrews.
31 import h5config as _h5config
32 from h5config.storage.hdf5 import HDF5_Storage as _HDF5_Storage
33 from h5config.storage.hdf5 import h5_create_group as _h5_create_group
34 except ImportError, e:
36 _h5py_import_error = e
39 import matplotlib as _matplotlib
40 import matplotlib.pyplot as _matplotlib_pyplot
41 except (ImportError, RuntimeError), e:
43 _matplotlib_import_error = e
45 from curses_check_for_keypress import CheckForKeypress as _CheckForKeypress
47 from . import LOG as _LOG
50 def _setup_config(peizo, config):
51 if config['wavelength'] and config['amplitude']:
53 'use either laser_wavelength or amplitude, but not both'
56 if None in (config['amplitude'], config['offset']):
57 output_axis = piezo.axis_by_name(config['axis'])
58 maxdata = output_axis.axis_channel.get_maxdata()
59 midpoint = int(maxdata/2)
60 if config['offset'] is None:
62 _LOG.debug(('generated offset for interference wiggle: {}'
63 ).format(config['offset']))
64 if config['amplitude'] is None:
65 if config['offset'] <= midpoint:
66 max_amplitude = int(config['offset'])
68 max_amplitude = int(maxdata-config['offset'])
69 offset_meters = _base.convert_bits_to_meters(
70 output_axis.config, config['offset'])
71 if config['wavelength'] is None:
72 config['amplitude'] = 0.5*max_amplitude
74 bit_wavelength = _base.convert_meters_to_bits(
76 offset_meters + config['wavelength']
78 config['amplitude'] = 2*bit_wavelength
79 _LOG.debug(('generated amplitude for interference wiggle: {}'
80 ).format(config['amplitude']))
81 if config['amplitude'] > max_amplitude:
83 'no room for a two wavelength wiggle ({} > {})'.format(
84 config['amplitude'], max_amplitude))
86 def _construct_output(piezo, config):
87 n = config['samples'] # samples in a single oscillation
88 out = (config['amplitude']
89 * _numpy.sin(_numpy.arange(2*n)*2*_numpy.pi/n)
91 # 2*n for 2 periods, so you can judge precision
92 out = out.reshape((len(out), 1)).astype(
93 piezo.channel_dtype(config['axis'], direction='output'))
96 def _setup_datafile(filename, group, piezo, config, output):
98 raise _h5py_import_error
99 output_axis = afm.piezo.axis_by_name(config['axis'])
100 input_channel = afm.piezo.input_channel_by_name(config['input'])
101 with _h5py.File(filename, 'w') as f:
102 cwg = _h5_create_group(f, group)
103 storage = _HDF5_Storage()
105 (config, 'config/wiggle'),
107 'config/{}/axis'.format(config['axis'])),
108 (input_channel.config,
109 'config/{}/channel'.format(config['input']))]:
112 config_cwg = _h5_create_group(cwg, key)
113 storage.save(config=config, group=config_cwg)
114 cwg['wiggle/raw/{}'.format(config['axis'])] = output
116 def _update_datafile(filename, group, config, cycle, data):
117 timestamp = ('{0}-{1:02d}-{2:02d}T{3:02d}-{4:02d}-{5:02d}'
118 ).format(*_time.localtime())
119 with _h5py.File(filename, 'a') as f:
120 wiggle_group = _h5_create_group(f, group)
121 cwg = _h5_create_group(
122 wiggle_group, 'wiggle/{}'.format(cycle))
123 cwg['time'] = timestamp
124 cwg['raw/{}'.format(config['input'])] = data
126 def _setup_plot(piezo, config):
128 raise _matplotlib_import_error
129 _matplotlib.interactive(True)
130 figure = _matplotlib_pyplot.figure()
131 axes = figure.add_subplot(1, 1, 1)
133 timestamp = _time.strftime('%H%M%S')
134 axes.set_title('wiggle for interference %s' % timestamp)
135 plot = axes.plot(out, out, 'b.-')
137 _matplotlib_pyplot.draw()
138 _matplotlib_pyplot.show()
141 def _update_plot(plot, cycle, data):
142 plot[0].set_ydata(data[:,0])
143 axes.set_ylim([data.min(), data.max()])
144 _matplotlib_pyplot.draw()
146 def _run_wiggles(piezo, config, plot, output, filename=None, group='/',
147 keypress_test_mode=False):
148 scan_frequency = config['frequency'] * n
150 c = _CheckForKeypress(test_mode=keypress_test_mode)
151 while c.input() == None:
152 # input will need processing for multi-segment AFMs...
154 output, scan_frequency, output_names=[config['axis']],
155 input_names=[config['input']])
156 _LOG.debug('completed a wiggle round')
159 filename=filename, group=group, config=config,
160 cycle=cycle, data=data)
162 _update_plot(plot=plot, cycle=cycle, data=data)
165 def wiggle_for_interference(
166 piezo, config, plot=True, filename=None, group='/',
167 keypress_test_mode=False):
168 """Output a sine wave and measure interference.
170 With a poorly focused or aligned laser, leaked laser light
171 reflecting off the surface may interfere with the light
172 reflected off the cantilever, causing distance-dependent
173 interference with a period roughly half the laser's
174 wavelength. This method wiggles the cantilever near the
175 surface and monitors the magnitude of deflection oscillation,
176 allowing the operator to adjust the laser alignment in order
177 to minimize the interference.
179 Modern commercial AFMs with computer-aligned lasers must do
180 something like this automatically.
182 if _package_config['matplotlib']:
184 _setup_config(piezo=piezo, config=config)
185 _LOG.debug('oscillate for interference wiggle ({})'.format(config))
186 output = _construct_output(piezo=piezo, config=config)
190 filename=filename, group=group, piezo=piezo, config=config,
193 interactive = _matplotlib.is_interactive()
194 plot_ = _setup_plot(piezo=piezo, config=config, output=output)
195 _run_wiggles(piezo=piezo, config=config, plot=plot_)
197 _matplotlib.interactive(interactive)
198 piezo.last_output[config['axis']] = out[-1,0]
199 _LOG.debug('interference wiggle complete')