"""Define classes for carrying out an unfolding cycle with an AFM."""
+from __future__ import division
+
import email.utils as _email_utils
import os.path as _os_path
import time as _time
try:
import numpy as _numpy
from matplotlib import pyplot as _pyplot
+ _pyplot.ion()
FIGURE = _pyplot.figure()
except (ImportError, RuntimeError), _matplotlib_import_error:
_pyplot = None
setpoint = deflection + config['relative setpoint']
_LOG.info('approach with setpoint = {}'.format(setpoint))
axis_config = self.afm.piezo.config.select_config(
- 'axes', self.afm.axis_name,
+ 'axes', self.afm.config['main-axis'],
get_attribute=_pypiezo_base.get_axis_name
)
def_config = self.afm.piezo.config.select_config(
'inputs', 'deflection')
- start_pos = self.afm.piezo.last_output[self.afm.axis_name]
+ start_pos = self.afm.piezo.last_output[self.afm.config['main-axis']]
# calculate parameters for move_to_pos_or_def from config
setpoint_bits = _pypiezo_base.convert_volts_to_bits(
# run the approach
data = self.afm.piezo.move_to_pos_or_def(
- axis_name=self.afm.axis_name, deflection=setpoint_bits,
+ axis_name=self.afm.config['main-axis'], deflection=setpoint_bits,
step=step_bits, frequency=frequency, return_data=True)
data['setpoint'] = setpoint
# check the output
_LOG.info(('unfolding too far from the surface '
'(def {} < target {})').format(
data['deflection'].max(), setpoint_bits))
- self.afm.piezo.jump(self.afm.axis_name, start_pos)
+ self.afm.piezo.jump(self.afm.config['main-axis'], start_pos)
if _package_config['matplotlib']:
print data
FIGURE.clear()
config = self.config['unfold']
velocity = config['velocity']
_LOG.info('unfold at {:g} m/s'.format(velocity))
- axis = self.afm.piezo.axis_by_name(self.afm.axis_name)
+ axis = self.afm.piezo.axis_by_name(self.afm.config['main-axis'])
axis_config = self.afm.piezo.config.select_config(
- 'axes', self.afm.axis_name,
+ 'axes', self.afm.config['main-axis'],
get_attribute=_pypiezo_base.get_axis_name
)
d = self.afm.piezo.channel_by_name('deflection')
def_config = self.afm.piezo.config.select_config(
'inputs', 'deflection')
- start_pos = self.afm.piezo.last_output[self.afm.axis_name]
+ start_pos = self.afm.piezo.last_output[self.afm.config['main-axis']]
start_pos_m = _pypiezo_base.convert_bits_to_meters(
axis_config, start_pos)
final_pos = _pypiezo_base.convert_meters_to_bits(
axis_config, final_pos_m)
dtype = self.afm.piezo.channel_dtype(
- self.afm.axis_name, direction='output')
+ self.afm.config['main-axis'], direction='output')
+ frequency = config['frequency']
num_steps = int(
- config['distance'] / config['velocity'] * config['frequency']) + 1
+ config['distance'] / config['velocity'] * frequency) + 1
# (m) * (s/m) * (samples/s)
+ max_samples = self._get_max_samples()
+ if num_steps > max_samples:
+ num_steps = max_samples
+ frequency = (num_steps - 1)*config['velocity']/config['distance']
+ _LOG.info(('limit frequency to {} Hz (from {} Hz) to fit in DAQ '
+ 'card buffer').format(frequency, config['frequency']))
+
out = _numpy.linspace(
start_pos, final_pos, num_steps).astype(dtype)
# TODO: check size of output buffer.
out = out.reshape((len(out), 1))
_LOG.debug(
'unfolding from {} to {} in {} steps at {} Hz'.format(
- start_pos, final_pos, num_steps, config['frequency']))
+ start_pos, final_pos, num_steps, frequency))
data = self.afm.piezo.ramp(
- data=out, frequency=config['frequency'],
- output_names=[self.afm.axis_name], input_names=['deflection'])
- return {self.afm.axis_name:out, 'deflection':data}
+ data=out, frequency=frequency, output_names=[self.afm.config['main-axis']],
+ input_names=['deflection'])
+ return {
+ 'frequency': frequency, self.afm.config['main-axis']:out, 'deflection':data}
+
+ def _get_max_samples(self):
+ """Return the maximum number of samples that will fit on the card.
+
+ `pycomedi.utility.Writer` seems to have trouble when the the
+ output buffer is bigger than the card's onboard memory, so
+ we reduce the frequency if neccessary to fit the scan in
+ memory.
+ """
+ axis = self.afm.piezo.axis_by_name(self.afm.config['main-axis'])
+ buffer_size = axis.axis_channel.subdevice.get_buffer_size()
+ dtype = self.afm.piezo.channel_dtype(
+ self.afm.config['main-axis'], direction='output')
+ # `channel_dtype` returns `numpy.uint16`, `numpy.uint32`,
+ # etc., which are "generic types". We use `numpy.dtype` to
+ # construct a `dtype` object:
+ # >>> import numpy
+ # >>> numpy.uint16
+ # <type 'numpy.uint16'>
+ # >>> numpy.dtype(numpy.uint16)
+ # dtype('uint16')
+ dt = _numpy.dtype(dtype)
+ sample_size = dt.itemsize
+ max_output_samples = buffer_size // sample_size
+ return max_output_samples
def _save(self, temperature, approach, unfold, timestamp):
config = self.config['save']
storage = _HDF5_Storage()
config_cwg = _h5_create_group(f, 'config')
storage.save(config=self.config, group=config_cwg)
+ afm_piezo_cwg = _h5_create_group(config_cwg, 'afm/piezo')
+ storage.save(config=self.afm.piezo.config, group=afm_piezo_cwg)
f['timestamp'] = timestamp
- f['temperature'] = temperature
+ if temperature is not None:
+ f['temperature'] = temperature
for k,v in approach.items():
f['approach/{}'.format(k)] = v
for k,v in unfold.items():
axes.hold(True)
axes.plot(approach['z'], approach['deflection'], label='Approach')
axes.plot(unfold['z'], unfold['deflection'], label='Unfold')
- axes.set_title('Unfolding too far')
axes.legend(loc='best')
axes.set_title('Unfolding')
+ _pyplot.draw()
_pyplot.show()
def zero_piezo(self):