40fe80f759bfe24660434e617e43f8a8b3e65519
[unfold-protein.git] / unfold_protein / scan.py
1 # Copyright (C) 2012 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of unfold_protein.
4 #
5 # unfold_protein is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option) any
8 # later version.
9 #
10 # unfold_protein is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13 # details.
14 #
15 # You should have received a copy of the GNU General Public License along with
16 # unfold_protein.  If not, see <http://www.gnu.org/licenses/>.
17
18 """Define `UnfoldScanner` for sequential unfolding experiments."""
19
20 import signal as _signal
21
22 import pypiezo.base as _pypiezo_base
23
24 from . import LOG as _LOG
25 from .unfolder import ExceptionTooFar as _ExceptionTooFar
26 from .unfolder import ExceptionTooClose as _ExceptionTooClose
27
28 class UnfoldScanner (object):
29     def __init__(self, config, unfolder):
30         self.config = config
31         self.unfolder = unfolder
32         self._state = {'x direction': 1}
33
34     def run(self, stepper_tweaks=True):
35         self._stop = False
36         _signal.signal(_signal.SIGTERM, self._handle_stop_signal)
37         self.unfolder.afm.move_away_from_surface()
38         self.stepper_approach()
39         for i in range(self.config['velocity']['num loops']):
40             _LOG.info('on loop {} of {}'.format(
41                     i, self.config['velocity']['num loops']))
42             for velocity in self.config['velocity']['unfolding velocities']:
43                 if self._stop:
44                     return
45                 self.unfolder.config['unfold']['velocity'] = velocity
46                 try:
47                     self.unfolder.run()
48                 except _ExceptionTooFar:
49                     if stepper_tweaks:
50                         self.stepper_approach()
51                     else:
52                         raise
53                 except _ExceptionTooClose:
54                     if stepper_tweaks:
55                         self.afm.move_away_from_surface()
56                         self.stepper_approach()
57                     else:
58                         raise
59                 else:
60                     self.position_scan_step()
61
62     def _handle_stop_signal(self, signal, frame):
63         self._stop = True
64
65     def stepper_approach(self):
66         config = self.unfolder.config['approach']
67         deflection = self.unfolder.read_deflection()
68         setpoint = deflection + config['relative setpoint']
69         def_config = self.unfolder.afm.piezo.config.select_config(
70             'inputs', 'deflection')
71         setpoint_bits = _pypiezo_base.convert_volts_to_bits(
72             def_config, setpoint)
73         self.unfolder.afm.stepper_approach(target_deflection=setpoint_bits)
74
75     def position_scan_step(self):
76         axis_name = 'x'
77         config = self.config['position'] 
78         axis_config = self.unfolder.afm.piezo.config.select_config(
79                 'axes', self.unfolder.afm.config['main-axis'],
80                 get_attribute=_pypiezo_base.get_axis_name
81                 )
82         pos = self.unfolder.afm.piezo.last_output[axis_name]
83         pos_m = _pypiezo_base.convert_bits_to_meters(axis_config, pos)
84         # HACK
85         try:
86             step = float(open('/home/wking/x-step', 'r').read())
87             _LOG.info('read step from file: {}'.format(step))
88         except Exception, e:
89             _LOG.warn('could not read step from file: {}'.format(e))
90             step = config['x step']
91         next_pos_m = pos_m + self._state['x direction']*step
92         if next_pos_m > config['x max']:
93             self._state['x direction'] = -1
94             next_pos_m = pos_m + self._state['x direction']*step
95         elif next_pos_m < config['x min']:
96             self._state['x direction'] = 1
97             next_pos_m = pos_m + self._state['x direction']*step
98         next_pos = _pypiezo_base.convert_meters_to_bits(
99             axis_config, next_pos_m)
100         _LOG.info('move {} from {:g} to {:g} bits'.format(
101                 axis_name, pos, next_pos))
102         self.unfolder.afm.piezo.jump(axis_name, next_pos)