Source code for place.plugins.new_focus.new_focus

"""Mirror movement using the New Focus picomotors."""
from itertools import cycle, repeat
from socket import timeout
from time import sleep

from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np

from place.config import PlaceConfig
from place.plugins.instrument import Instrument

from . import pmot
from .pmot import PMot


[docs]class Picomotor(Instrument): """The picomotor class.""" def __init__(self, config, plotter): """Initialize the controller, without configuring. :param config: configuration data (from JSON) :type config: dict :param plotter: a plotting object to return plots to the web interface :type plotter: plots.PlacePlotter """ Instrument.__init__(self, config, plotter) self._controller = None self._position = None self.last_x = None self.last_y = None self.fig = None self.ax = None
[docs] def config(self, metadata, total_updates): """Configure the picomotors for an experiment. :param metadata: metadata for the experiment :type metadata: dict :param total_updates: the number of update steps that will be in this experiment :type total_updates: int """ self._configure_controller() self._create_position_iterator(total_updates)
[docs] def update(self, update_number, progress): """Move the mirror. :param update_number: the current update count :type update_number: int :param progress: the PLACE values sent to your web application :type progress: dict :returns: the position data collected :rtype: numpy.array """ x_position, y_position = self._move_picomotors() x_field = '{}-x_position'.format(self.__class__.__name__) y_field = '{}-y_position'.format(self.__class__.__name__) data = np.array( [(x_position, y_position)], dtype=[(x_field, 'int32'), (y_field, 'int32')]) if self._config['plot']: self._make_position_plot(data, update_number) sleep(self._config['sleep_time']) return data
[docs] def cleanup(self, abort=False): """Stop picomotor and end experiment. :param abort: indicates the experiment is being aborted rather than having finished normally :type abort: bool """ self._controller.close()
# PRIVATE METHODS def _configure_controller(self): """Send all the starting configurations to the picomotors.""" name = self.__class__.__name__ ip_address = PlaceConfig().get_config_value(name, "ip_address") port = PlaceConfig().get_config_value(name, "port") self._controller = PMot() self._controller.connect(ip_address, int(port)) self._controller.set_velocity(pmot.PX, 1700) self._controller.set_velocity(pmot.PY, 1700) self._controller.set_axis_displacement(pmot.PX, 1) self._controller.set_axis_displacement(pmot.PY, 1) self._controller.set_following_error(pmot.PX, 200) self._controller.set_following_error(pmot.PY, 200) self._controller.set_cl(pmot.PX, 0.1) self._controller.set_cl(pmot.PY, 0.1) self._controller.set_mm(pmot.PX, 1) self._controller.set_mm(pmot.PY, 1) self._controller.set_sm() def _create_position_iterator(self, total_updates): """Create a Python iterator object to control motion. Each time next() is called on this object, it will return the next x,y position. :param total_updates: the number of update steps that will be in this experiment :type total_updates: int :raises ValueError: if an invalid shape is requested in the JSON configuration """ if self._config['shape'] == 'point': x_one = self._config['x_one'] y_one = self._config['y_one'] self._position = repeat((x_one, y_one), total_updates) elif self._config['shape'] == 'line': x_one = self._config['x_one'] y_one = self._config['y_one'] x_two = self._config['x_two'] y_two = self._config['y_two'] x_delta = (x_two - x_one) / (total_updates - 1) y_delta = (y_two - y_one) / (total_updates - 1) self._position = ((x_one + i * x_delta, y_one + i * y_delta) for i in np.arange(total_updates)) elif self._config['shape'] == 'circle': x_one = self._config['x_one'] y_one = self._config['y_one'] rho = self._config['radius'] phi_delta = 2 * np.pi / total_updates self._position = (polar_to_cart(rho, phi) for phi in np.arange(0, 2*np.pi, phi_delta)) elif self._config['shape'] == 'arc': x_one = self._config['x_one'] y_one = self._config['y_one'] rho = self._config['radius'] phi_delta = 2 * np.pi / self._config['sectors'] self._position = cycle( polar_to_cart(rho, phi) for phi in np.arange(0, 2*np.pi, phi_delta) ) for _ in range(self._config['starting_sector']): next(self._position) else: raise ValueError('unrecognized shape') def _move_picomotors(self): """Move the picomotors. :returns: the x and y positions of the motors :rtype: (int, int) :raises RuntimeError: if movement fails """ tries = 25 pause = 10 x_position, y_position = next(self._position) for i in range(tries): try: if i > 0: print('starting attempt number {} of {}'.format(i+1, tries)) self._configure_controller() x_result, y_result = self._controller.absolute_move( x_position, y_position) return x_result, y_result except OSError: print('could not connect to picomotor controller', end="") print('- will retry in {} seconds'.format(pause)) except timeout: print('a timeout occurred - will restart in {} seconds'.format(pause)) self._controller.close() if i >= tries - 1: raise RuntimeError('could not communicate with picomotors') sleep(pause) def _make_position_plot(self, data, update_number): """Plot the x,y position throughout the experiment. :param data: the data to display on the plot :type data: numpy.array :param update_number: the current update :type update_number: int :returns: the PLACE plot :rtype: dict """ name = self.__class__.__name__ if update_number == 0: self.fig = Figure(figsize=(7.29, 4.17), dpi=96) FigureCanvas(self.fig) self.ax = self.fig.add_subplot(111) if self._config['invert_x']: self.ax.invert_xaxis() if self._config['invert_y']: self.ax.invert_yaxis() self.ax.axis('equal') curr_x = data[0]['{}-x_position'.format(name)] curr_y = data[0]['{}-y_position'.format(name)] self.ax.plot(curr_x, curr_y, '-o') self.last_x = curr_x self.last_y = curr_y else: curr_x = data[0]['{}-x_position'.format(name)] curr_y = data[0]['{}-y_position'.format(name)] self.ax.plot([self.last_x, curr_x], [self.last_y, curr_y], '-o') self.last_x = curr_x self.last_y = curr_y return self.plotter.png( 'Picomotor motion', self.fig, alt='Plot showing the movement of the picomotors' )
[docs]def polar_to_cart(rho, phi): """Convert polar to cartesian""" return rho * np.cos(phi), rho * np.sin(phi)