import os
import os.path
-from calibcant.config import Kelvin as _Kelvin
-from unfold_protein import __version__ as version
-from unfold_protein.afm import get_afm
+import numpy as _numpy
+
+from pyafm.storage import load_afm as _load_afm
+from pyafm.config import Kelvin as _Kelvin
+from unfold_protein import __version__
from unfold_protein.unfolder import Unfolder
from unfold_protein.scan import UnfoldScanner
import unfold_protein.config as _config
from argparse import ArgumentParser
parser = ArgumentParser(
- description='Play a pure tone', version=version)
+ description=__doc__, version=__version__)
parser.add_argument(
'-s', '--song',
help='Path to a song to play when the experiment is complete')
args = parser.parse_args()
unfold_config = _config.UnfoldCycleConfig()
- unfold_config['temperature'] = _config.TemperatureConfig()
- unfold_config['temperature']['units'] = _Kelvin
unfold_config['approach'] = _config.ApproachConfig()
unfold_config['unfold'] = _config.UnfoldConfig()
+ unfold_config['unfold']['distance'] = 900e-9
unfold_config['save'] = _config.SaveConfig()
scan_config = _config.ScanConfig()
scan_config['velocity'] = _config.VelocityScanConfig()
+ #scan_config['velocity']['unfolding velocities'] = _numpy.array([1e-6])
+ scan_config['velocity']['num loops'] = 500
scan_config['position'] = _config.PositionScanConfig()
- afm,comedi_device = get_afm(with_temperature=False)
- unfolder = Unfolder(config=unfold_config, afm=afm)
- scanner = UnfoldScanner(config=scan_config, unfolder=unfolder)
+ devices = []
try:
+ afm = _load_afm()
+ afm.load_from_config(devices=devices)
+ afm.piezo.zero()
+ unfolder = Unfolder(config=unfold_config, afm=afm)
+ scanner = UnfoldScanner(config=scan_config, unfolder=unfolder)
scanner.run()
finally:
- scanner.move_far_from_surface()
- comedi_device.close()
+ afm.move_away_from_surface()
+ afm.piezo.zero()
+ for device in devices:
+ device.close()
if args.song:
song = os.path.abspath(os.path.expanduser(args.song))
os.system("aplay '%s'" % song)
import signal as _signal
-from calibcant.calibrate import move_far_from_surface as _move_far_from_surface
import pypiezo.base as _pypiezo_base
from . import LOG as _LOG
except _ExceptionTooFar:
self.stepper_approach()
except _ExceptionTooClose:
- self.move_far_from_surface()
+ self.afm.move_far_from_surface()
self.stepper_approach()
else:
self.position_scan_step()
def _handle_stop_signal(self, signal, frame):
self._stop = True
- def move_far_from_surface(self):
- _LOG.info('retract with the stepper motor by {} m'.format(
- self.unfolder.config['approach']['far']))
- _move_far_from_surface(
- stepper=self.unfolder.afm.stepper,
- distance=self.unfolder.config['approach']['far'])
-
def stepper_approach(self):
config = self.unfolder.config['approach']
deflection = self.unfolder.read_deflection()
axis_name = 'x'
config = self.config['position']
axis_config = self.unfolder.afm.piezo.config.select_config(
- 'axes', self.unfolder.afm.axis_name,
+ 'axes', self.unfolder.afm.config['main-axis'],
get_attribute=_pypiezo_base.get_axis_name
)
pos = self.unfolder.afm.piezo.last_output[axis_name]
pos_m = _pypiezo_base.convert_bits_to_meters(axis_config, pos)
- next_pos_m = pos_m + self._state['x direction']*config['x step']
+ # HACK
+ try:
+ step = float(open('/home/wking/x-step', 'r').read())
+ _LOG.info('read step from file: {}'.format(step))
+ except Exception, e:
+ _LOG.warn('could not read step from file: {}'.format(e))
+ step = config['x step']
+ next_pos_m = pos_m + self._state['x direction']*step
if next_pos_m > config['x max']:
self._state['x direction'] = -1
- next_pos_m = pos_m + self._state['x direction']*config['x step']
+ next_pos_m = pos_m + self._state['x direction']*step
elif next_pos_m < config['x min']:
self._state['x direction'] = 1
- next_pos_m = pos_m + self._state['x direction']*config['x step']
+ next_pos_m = pos_m + self._state['x direction']*step
next_pos = _pypiezo_base.convert_meters_to_bits(
axis_config, next_pos_m)
_LOG.info('move {} from {:g} to {:g} bits'.format(
"""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):