Exemplo n.º 1
0
class FooDeviceTargetValue(BaseDevice):
    foo = Quantity(q.mm)

    def __init__(self, value):
        super(BaseDevice, self).__init__()
        self._value = value
        self._test_value = value

    async def _get_foo(self):
        delta = 1 * q.mm
        return self._value + delta

    async def _set_foo(self, value):
        self._value = value

    async def _get_target_foo(self):
        return self._value

    def test_setter(self, value):
        self._test_value = value

    def test_getter(self):
        return self._test_value

    def test_target(self):
        return 10 * q.mm

    test = Quantity(q.mm,
                    fset=_test_setter,
                    fget=_test_getter,
                    fget_target=_test_target)
Exemplo n.º 2
0
class FooDevice(BaseDevice):

    state = State(default='standby')

    no_write = Parameter()
    foo = Quantity(q.m, check=check(source='*', target='moved'))
    bar = Quantity(q.m)
    test = Quantity(q.m, fset=_test_setter, fget=_test_getter)

    def __init__(self, default):
        super(FooDevice, self).__init__()
        self._value = default
        self._param_value = 0 * q.mm
        self._test_value = 0 * q.mm

    async def _get_foo(self):
        return self._value

    @transition(target='moved')
    async def _set_foo(self, value):
        self._value = value

    async def _get_bar(self):
        return 5 * q.m

    param = Parameter(fget=_get_foo, fset=_set_foo)

    async def _get_param(self):
        return self._param_value

    async def _set_param(self, value):
        self._param_value = value

    param = Parameter(fget=_get_param, fset=_set_param)
Exemplo n.º 3
0
class StorageRing(Device):
    """Read-only access to storage ring information.

    .. py:attribute:: current

        Ring current

    .. py:attribute:: energy

        Ring energy

    .. py:attribute:: lifetime

        Ring lifetime in hours
    """
    current = Quantity(q.mA, help="Current")
    energy = Quantity(q.MeV, help="Energy")
    lifetime = Quantity(q.hour, help="Expected lifetime")

    def __init__(self):
        super(StorageRing, self).__init__()

    async def _get_current(self):
        raise AccessorNotImplementedError

    async def _get_energy(self):
        raise AccessorNotImplementedError

    async def _get_lifetime(self):
        raise AccessorNotImplementedError
Exemplo n.º 4
0
class SpiralMixin(Parameterizable):
    """
    Mixin for spiral tomography.
    """
    start_position_vertical = Quantity(q.mm)
    """Initial position of the vertical motor."""

    vertical_shift_per_tomogram = Quantity(q.mm)
    """Vertical shift per tomogram."""

    sample_height = Quantity(q.mm)
    """Height of the sample. *vertical_motor* will be scanned from *start_position_vertical* to
    *sample_height* + *vertical_shift_per_tomogram* to sample the whole specimen.
    """

    num_tomograms = Parameter()
    """Number of tomograms, that are required to cover the whole specimen."""
    def __init__(self, vertical_motor, start_position_vertical, sample_height,
                 vertical_shift_per_tomogram):
        """
        :param vertical_motor: LinearMotor to translate the sample along the tomographic axis.
        :type vertical_motor: concert.devices.motors.base.LinearMotor
        :param start_position_vertical: Start position of *vertical_motor*.
        :type start_position_vertical: q.mm
        :param sample_height: Height of the sample.
        :type sample_height: q.mm
        :param vertical_shift_per_tomogram: Distance *vertical_motor* is translated during one
            *angular_range*.
        :type vertical_shift_per_tomogram: q.mm
        """
        self._start_position_vertical = start_position_vertical
        self._vertical_shift_per_tomogram = vertical_shift_per_tomogram
        self._sample_height = sample_height
        self._vertical_motor = vertical_motor
        Parameterizable.__init__(self)

    async def _get_start_position_vertical(self):
        return self._start_position_vertical

    async def _get_num_tomograms(self):
        shift = await self.get_vertical_shift_per_tomogram()
        height = await self.get_sample_height()

        return abs(height / shift).to_base_units().magnitude + 1

    async def _get_vertical_shift_per_tomogram(self):
        return self._vertical_shift_per_tomogram

    async def _get_sample_height(self):
        return self._sample_height

    async def _set_start_position_vertical(self, position):
        self._start_position_vertical = position

    async def _set_sample_height(self, height):
        self._sample_height = height

    async def _set_vertical_shift_per_tomogram(self, shift):
        self._vertical_shift_per_tomogram = shift
Exemplo n.º 5
0
class Monochromator(Device):
    """Monochromator device which is used to filter the beam in order to
    get a very narrow energy bandwidth.

    .. py:attribute:: energy

        Monochromatic energy in electron volts.

    .. py:attribute:: wavelength

        Monochromatic wavelength in meters.
    """

    state = State(default="standby")
    energy = Quantity(q.eV, help="Energy")
    wavelength = Quantity(q.nanometer, help="Wavelength")

    async def _get_energy(self):
        try:
            return await self._get_energy_real()
        except AccessorNotImplementedError:
            return wavelength_to_energy(await self._get_wavelength_real())

    @check(source="standby", target="standby")
    async def _set_energy(self, energy):
        try:
            await self._set_energy_real(energy)
        except AccessorNotImplementedError:
            await self._set_wavelength_real(energy_to_wavelength(energy))

    async def _get_wavelength(self):
        try:
            return await self._get_wavelength_real()
        except AccessorNotImplementedError:
            return energy_to_wavelength(await self._get_energy_real())

    @check(source="standby", target="standby")
    async def _set_wavelength(self, wavelength):
        try:
            await self._set_wavelength_real(wavelength)
        except AccessorNotImplementedError:
            await self._set_energy_real(wavelength_to_energy(wavelength))

    async def _get_energy_real(self):
        raise AccessorNotImplementedError

    async def _set_energy_real(self, energy):
        raise AccessorNotImplementedError

    async def _get_wavelength_real(self):
        raise AccessorNotImplementedError

    async def _set_wavelength_real(self, wavelength):
        raise AccessorNotImplementedError
Exemplo n.º 6
0
class UserLimitDevice(BaseDevice):
    """A device which tunnles the limit setting via user-defined functions."""

    foo = Quantity(q.mm)

    def __init__(self):
        super().__init__()
        self['foo']._user_lower_getter = self.get_user_lower
        self['foo']._user_lower_setter = self.set_user_lower
        self['foo']._user_upper_getter = self.get_user_upper
        self['foo']._user_upper_setter = self.set_user_upper
        self.lower_via_func = None
        self.upper_via_func = None

    async def get_user_lower(self):
        return self.lower_via_func

    async def set_user_lower(self, value):
        self.lower_via_func = value

    async def get_user_upper(self):
        return self.upper_via_func

    async def set_user_upper(self, value):
        self.upper_via_func = value
Exemplo n.º 7
0
class ExternalLimitDevice(BaseDevice):
    def __init__(self, value):
        super(ExternalLimitDevice, self).__init__()
        self._value = value

    foo = Quantity(q.mm,
                   external_lower_getter=get_external_lower,
                   external_upper_getter=get_external_upper)
Exemplo n.º 8
0
class Scales(Device):
    """Base scales class."""
    weight = Quantity(q.g, help="Weighted mass")

    def __init__(self):
        super(Scales, self).__init__()

    async def _get_weight(self):
        raise AccessorNotImplementedError
Exemplo n.º 9
0
class FooDeviceTagetValue(BaseDevice):
    foo = Quantity(q.mm)
    test = Quantity(q.mm,
                    fset=_test_setter,
                    fget=_test_getter,
                    fget_target=_test_target)

    def __init__(self, value):
        super(BaseDevice, self).__init__()
        self._value = value

    def _get_foo(self):
        delta = np.random.randint(low=1, high=100) * q.mm
        return self._value + delta

    def _set_foo(self, value):
        self._value = value

    def _get_target_foo(self):
        return self._value
Exemplo n.º 10
0
class PhotoDiode(Device):
    """
    Impementation of photo diode with V output signal
    """

    intensity = Quantity(q.V)

    def __init__(self):
        super(PhotoDiode, self).__init__()

    def _get_intensity(self):
        raise AccessorNotImplementedError
Exemplo n.º 11
0
class LightSource(Device):
    """A base LightSource class."""

    intensity = Quantity(q.V)

    def __init__(self):
        super(LightSource, self).__init__()

    async def _set_intensity(self, value):
        raise AccessorNotImplementedError

    async def _get_intensity(self):
        raise AccessorNotImplementedError
Exemplo n.º 12
0
class Detector(Device):
    """
    A base detector class for holding cameras and optics necessary to
    do acquire image.
    """

    pixel_width = Quantity(q.m, help="Effective pixel width")
    pixel_height = Quantity(q.m, help="Effective pixel height")

    def _get_pixel_width(self):
        return self.camera.sensor_pixel_width * self.magnification

    def _get_pixel_height(self):
        return self.camera.sensor_pixel_height * self.magnification

    @property
    def camera(self):
        raise AccessorNotImplementedError

    @property
    def magnification(self):
        raise AccessorNotImplementedError
Exemplo n.º 13
0
class RotationMotor(_PositionMixin, base.RotationMotor):
    """A rotational step motor dummy."""

    motion_velocity = Quantity(q.deg / q.s)

    def __init__(self, upper_hard_limit=None, lower_hard_limit=None):
        base.RotationMotor.__init__(self)
        _PositionMixin.__init__(self)
        self._position = 0 * q.deg
        self._lower_hard_limit = -np.inf * q.deg
        self._upper_hard_limit = np.inf * q.deg
        self._motion_velocity = 50000 * q.deg / q.s
        if upper_hard_limit:
            self._upper_hard_limit = upper_hard_limit
        if lower_hard_limit:
            self._lower_hard_limit = lower_hard_limit
Exemplo n.º 14
0
class AccessorCheckDevice(Parameterizable):

    foo = Quantity(q.m)

    def __init__(self, future, check):
        super(AccessorCheckDevice, self).__init__()
        self.check = check
        self.future = future
        self._value = 0 * q.mm

    async def _set_foo(self, value):
        self.check(self.future)
        self._value = value
        time.sleep(0.01)

    async def _get_foo(self):
        self.check(self.future)
        time.sleep(0.01)
        return self._value
Exemplo n.º 15
0
class LinearMotor(_PositionMixin, base.LinearMotor):
    """A linear step motor dummy."""

    motion_velocity = Quantity(q.mm / q.s)

    def __init__(self,
                 position=None,
                 upper_hard_limit=None,
                 lower_hard_limit=None):
        base.LinearMotor.__init__(self)
        _PositionMixin.__init__(self)
        self._motion_velocity = 200000 * q.mm / q.s

        if position:
            self._position = position
        if upper_hard_limit:
            self._upper_hard_limit = upper_hard_limit
        if lower_hard_limit:
            self._lower_hard_limit = lower_hard_limit
Exemplo n.º 16
0
class RotationMotor(_PositionMixin):
    """
    One-dimensional rotational motor.

    .. attribute:: position

        Position of the motor in angular units.
    """
    async def _get_state(self):
        raise NotImplementedError

    state = State(default='standby')

    position = Quantity(q.deg,
                        help="Angular position",
                        check=check(source=['hard-limit', 'standby'],
                                    target=['hard-limit', 'standby']))

    def __init__(self):
        super(RotationMotor, self).__init__()
Exemplo n.º 17
0
class ContinuousRotationMotor(RotationMotor, _VelocityMixin):
    """
    One-dimensional rotational motor with adjustable velocity.

    .. attribute:: velocity

        Current velocity in angle per time unit.
    """
    def __init__(self):
        super(ContinuousRotationMotor, self).__init__()

    async def _get_state(self):
        raise NotImplementedError

    state = State(default='standby')

    velocity = Quantity(q.deg / q.s,
                        help="Angular velocity",
                        check=check(source=['hard-limit', 'standby', 'moving'],
                                    target=['moving', 'standby']))
Exemplo n.º 18
0
class LinearMotor(_PositionMixin):
    """
    One-dimensional linear motor.

    .. attribute:: position

        Position of the motor in length units.
    """
    def __init__(self):
        super(LinearMotor, self).__init__()

    def _get_state(self):
        raise NotImplementedError

    state = State(default='standby')

    position = Quantity(q.mm,
                        help="Position",
                        check=check(source=['hard-limit', 'standby'],
                                    target=['hard-limit', 'standby']))
Exemplo n.º 19
0
class Pump(Device):
    """A pumping device."""

    state = State(default='standby')
    flow_rate = Quantity(q.l / q.s, help="Flow rate")

    def __init__(self):
        super(Pump, self).__init__()

    @async
    @check(source='standby', target='pumping')
    def start(self):
        """
        start()

        Start pumping.
        """
        self._start()

    @async
    @check(source='pumping', target='standby')
    def stop(self):
        """
        stop()

        Stop pumping.
        """
        self._stop()

    def _get_flow_rate(self):
        raise AccessorNotImplementedError

    def _set_flow_rate(self, flow_rate):
        raise AccessorNotImplementedError

    def _start(self):
        raise NotImplementedError

    def _stop(self):
        raise NotImplementedError
Exemplo n.º 20
0
class ContinuousLinearMotor(LinearMotor):
    """
    One-dimensional linear motor with adjustable velocity.

    .. attribute:: velocity

        Current velocity in length per time unit.
    """
    def __init__(self):
        super(ContinuousLinearMotor, self).__init__()

    def _get_state(self):
        raise NotImplementedError

    def _cancel_velocity(self):
        self._abort()

    state = State(default='standby')

    velocity = Quantity(q.mm / q.s,
                        help="Linear velocity",
                        check=check(source=['hard-limit', 'standby', 'moving'],
                                    target=['moving', 'standby']))
Exemplo n.º 21
0
class Base(base.Camera):

    exposure_time = Quantity(q.s)
    sensor_pixel_width = Quantity(q.micrometer)
    sensor_pixel_height = Quantity(q.micrometer)
    roi_x0 = Quantity(q.pixel)
    roi_y0 = Quantity(q.pixel)
    roi_width = Quantity(q.pixel)
    roi_height = Quantity(q.pixel)

    def __init__(self):
        super(Base, self).__init__()
        self._frame_rate = 1000 / q.s
        self._trigger_source = self.trigger_sources.AUTO
        self._exposure_time = 1 * q.ms
        self._roi_x0 = 0 * q.pixel
        self._roi_y0 = 0 * q.pixel
        self._roi_width = 640 * q.pixel
        self._roi_height = 480 * q.pixel

    async def _get_sensor_pixel_width(self):
        return 5 * q.micrometer

    async def _get_sensor_pixel_height(self):
        return 5 * q.micrometer

    async def _get_roi_x0(self):
        return self._roi_x0

    async def _set_roi_x0(self, x0):
        self._roi_x0 = x0

    async def _get_roi_y0(self):
        return self._roi_y0

    async def _set_roi_y0(self, y0):
        self._roi_y0 = y0

    async def _get_roi_width(self):
        return self._roi_width

    async def _set_roi_width(self, roi):
        self._roi_width = roi

    async def _get_roi_height(self):
        return self._roi_height

    async def _set_roi_height(self, roi):
        self._roi_height = roi

    async def _get_exposure_time(self):
        return self._exposure_time

    async def _set_exposure_time(self, value):
        self._exposure_time = value

    async def _get_frame_rate(self):
        return self._frame_rate

    async def _set_frame_rate(self, frame_rate):
        self._frame_rate = frame_rate

    async def _get_trigger_source(self):
        return self._trigger_source

    async def _set_trigger_source(self, source):
        self._trigger_source = source

    @transition(target='recording')
    async def _record_real(self):
        pass

    @transition(target='standby')
    async def _stop_real(self):
        pass

    async def _trigger_real(self):
        pass
Exemplo n.º 22
0
class Camera(Device):
    """Base class for remotely controllable cameras.

    .. py:attribute:: frame-rate

        Frame rate of acquisition in q.count per time unit.
    """

    trigger_sources = Bunch(['AUTO', 'SOFTWARE', 'EXTERNAL'])
    trigger_types = Bunch(['EDGE', 'LEVEL'])
    state = State(default='standby')
    frame_rate = Quantity(1 / q.second, help="Frame frequency")
    trigger_source = Parameter(help="Trigger source")

    def __init__(self):
        super(Camera, self).__init__()
        self.convert = identity

    @background
    @check(source='standby', target='recording')
    async def start_recording(self):
        """
        start_recording()

        Start recording frames.
        """
        await self._record_real()

    @background
    @check(source='recording', target='standby')
    async def stop_recording(self):
        """
        stop_recording()

        Stop recording frames.
        """
        await self._stop_real()

    @contextlib.asynccontextmanager
    async def recording(self):
        """
        recording()

        A context manager for starting and stopping the camera.

        In general it is used with the ``async with`` keyword like this::

            async with camera.recording():
                frame = await camera.grab()
        """
        await self.start_recording()
        try:
            yield
        finally:
            LOG.log(AIODEBUG, 'stop recording in recording()')
            await self.stop_recording()

    @background
    async def trigger(self):
        """Trigger a frame if possible."""
        await self._trigger_real()

    @background
    async def grab(self):
        """Return a NumPy array with data of the current frame."""
        return self.convert(await self._grab_real())

    async def stream(self):
        """
        stream()

        Grab frames continuously yield them. This is an async generator.
        """
        await self.set_trigger_source(self.trigger_sources.AUTO)
        await self.start_recording()

        while await self.get_state() == 'recording':
            yield await self.grab()

    async def _get_trigger_source(self):
        raise AccessorNotImplementedError

    async def _set_trigger_source(self, source):
        raise AccessorNotImplementedError

    async def _record_real(self):
        raise AccessorNotImplementedError

    async def _stop_real(self):
        raise AccessorNotImplementedError

    async def _trigger_real(self):
        raise AccessorNotImplementedError

    async def _grab_real(self):
        raise AccessorNotImplementedError
Exemplo n.º 23
0
class Radiography(Experiment):
    """
    Radiography experiment

    This records dark images (without beam) and flat images (with beam and without the sample) as
    well as the projections with the sample in the beam.
    """

    num_flats = Parameter()
    """Number of images acquired for flatfield correction."""

    num_darks = Parameter()
    """Number of images acquired for dark correction."""

    num_projections = Parameter()
    """Number of projection images."""

    radio_position = Quantity(q.mm)
    """Position of the flat_motor to acquire projection images of the sample."""

    flat_position = Quantity(q.mm)
    """Position of the flat_motor to acquire images without the sample."""
    def __init__(self,
                 walker,
                 flat_motor,
                 radio_position,
                 flat_position,
                 camera,
                 num_flats,
                 num_darks,
                 num_projections,
                 separate_scans=True):
        """
        :param walker: Walker for storing experiment data.
        :type walker: concert.storage.Walker
        :param flat_motor: LinearMotor for moving sample in and out of the beam.
        :type flat_motor: concert.devices.motors.base.LinearMotor
        :param radio_position: Position of *flat_motor* that the sample is positioned in the beam.
        :type radio_position: q.mm
        :param flat_position: Position of *flat_motor* that the sample is positioned out of the
            beam.
        :type flat_position: q.mm
        :param camera: Camera to acquire the images.
        :type camera: concert.devices.cameras.base.Camera
        :param num_flats: Number of images for flatfield correction.
        :type num_flats: int
        :param num_darks: Number of images for dark correction.
        :type num_darks: int
        :param num_projections: Number of projections.
        :type num_projections: int
        """
        self._num_flats = num_flats
        self._num_darks = num_darks
        self._num_projections = num_projections
        self._radio_position = radio_position
        self._flat_position = flat_position
        self._finished = None
        self._flat_motor = flat_motor
        self._camera = camera
        darks_acq = Acquisition("darks", self._take_darks)
        flats_acq = Acquisition("flats", self._take_flats)
        radios_acq = Acquisition("radios", self._take_radios)
        super().__init__([darks_acq, flats_acq, radios_acq],
                         walker,
                         separate_scans=separate_scans)

    async def _last_acquisition_running(self) -> bool:
        return await self.acquisitions[-1].get_state() == "running"

    async def _get_num_flats(self):
        return self._num_flats

    async def _get_num_darks(self):
        return self._num_darks

    async def _get_num_projections(self):
        return self._num_projections

    async def _get_radio_position(self):
        return self._radio_position

    async def _get_flat_position(self):
        return self._flat_position

    async def _set_num_flats(self, n):
        self._num_flats = int(n)

    async def _set_num_darks(self, n):
        self._num_darks = int(n)

    async def _set_num_projections(self, n):
        self._num_projections = int(n)

    async def _set_flat_position(self, position):
        self._flat_position = position

    async def _set_radio_position(self, position):
        self._radio_position = position

    async def _prepare_flats(self):
        """
        Called before flat images are acquired.

        The *flat_motor* is moved to the *flat_position* and :py:meth:`.start_sample_exposure()`
        will be called.
        """
        await self._flat_motor.set_position(await self.get_flat_position())
        await self.start_sample_exposure()

    async def _finish_flats(self):
        """
        Called after all flat images are acquired.

        Does nothing in this class.
        """
        pass

    async def _prepare_darks(self):
        """
        Called before the dark images are acquired.

        Calls :py:meth:`.stop_sample_exposure()`.
        """
        await self.stop_sample_exposure()

    async def _finish_darks(self):
        """
        Called after all dark images are acquired.

        Does nothing in this class.
        """
        pass

    async def _prepare_radios(self):
        """
        Called before the projection images are acquired.

        The *flat_motor* is moved to the *radio_position* and :py:meth:`.start_sample_exposure()`
        will be called.
        """
        await self._flat_motor.set_position(await self.get_radio_position())
        await self.start_sample_exposure()

    async def _finish_radios(self):
        """
        Function that is called after all frames are acquired. It will be called only once.

        This calls :py:meth:`.stop_sample_exposure()`.
        """
        if self._finished:
            return
        await self.stop_sample_exposure()
        self._finished = True

    @background
    async def run(self):
        self._finished = False
        await super().run()

    async def _take_radios(self):
        """
        Generator for projection images.

        First :py:meth:`._prepare_radios()` is called. Afterwards :py:meth:`._produce_frames()`
        generates the frames.
        At the end :py:meth:`._finish_radios()` is called.
        """
        try:
            await self._prepare_radios()
            async for frame in self._produce_frames(self._num_projections):
                yield frame
        finally:
            await self._finish_radios()

    async def _take_flats(self):
        """
        Generator for taking flatfield images

        First :py:meth:`._prepare_flats()` is called. Afterwards :py:meth:`._produce_frames()`
        generates the frames.
        At the end :py:meth:`._finish_flats()` is called.
        """
        try:
            await self._prepare_flats()
            async for frame in self._produce_frames(self._num_flats):
                yield frame
        finally:
            await self._finish_flats()

    async def _take_darks(self):
        """
        Generator for taking dark images

        First :py:meth:`._prepare_darks()` is called. Afterwards :py:meth:`._produce_frames()`
        generates the frames.
        At the end :py:meth:`._finish_darks()` is called.
        """
        try:
            await self._prepare_darks()
            async for frame in self._produce_frames(self._num_darks):
                yield frame
        finally:
            await self._finish_darks()

    async def _produce_frames(self, number, **kwargs):
        """
        Generator of frames.
        Sets the camera to auto-trigger and then grabs *number* of frames.

        :param number: Number of frames that are generated
        :type number: int
        """
        await self._camera.set_trigger_source("AUTO")
        async with self._camera.recording():
            for i in range(int(number)):
                yield await self._camera.grab()

    async def start_sample_exposure(self):
        """
        This function must implement in a way that the sample is exposed by radiation, like opening
        a shutter or starting an X-ray tube.
        """
        raise NotImplementedError()

    async def stop_sample_exposure(self):
        """
        This function must implement in a way that the sample is not exposed by radiation, like
        closing a shutter or switching off an X-ray tube.
        """
        raise NotImplementedError()
Exemplo n.º 24
0
class Positioner(Device):
    """Combines more motors which move to form a complex motion. *axes* is a list
    of :class:`.Axis` instances. *position* is a 3D vector of coordinates specifying
    the global position of the positioner.

    If a certain coordinate in the positioner is missing, then when we set the
    position or orientation we can specify the respective vector position to
    be zero or numpy.nan.
    """

    position = Quantity(q.m,
                        help="Global position",
                        lower=-INF_VECTOR * q.m,
                        upper=INF_VECTOR * q.m)

    orientation = Quantity(q.rad,
                           help="Orientation of the coordinate system",
                           lower=-INF_VECTOR * q.rad,
                           upper=INF_VECTOR * q.rad)

    def __init__(self, axes, position=None):
        super(Positioner, self).__init__()
        self.translators = {}
        self.rotators = {}
        self.global_position = None

        for axis in axes:
            if isinstance(axis.motor, LinearMotor):
                self.translators[axis.coordinate] = axis
            else:
                self.rotators[axis.coordinate] = axis

    @background
    async def move(self, position):
        """
        move(position)

        Move by specified *position*.
        """
        await self.set_position(await self.get_position() + position)

    @background
    async def rotate(self, angles):
        """
        rotate(angles)

        Rotate by *angles*.
        """
        await self.set_orientation(await self.get_orientation() + angles)

    @background
    async def right(self, value):
        """
        right(value)

        Move right by *value*."""
        return await self.move(_vectorize(value, 'x'))

    @background
    async def left(self, value):
        """
        left(value)

        Move left by *value*."""
        return await self.right(-value)

    @background
    async def up(self, value):
        """
        up(value)

        Move up by *value*.
        """
        return await self.move(_vectorize(value, 'y'))

    @background
    async def down(self, value):
        """
        down(value)

        Move down by *value*.
        """
        return await self.up(-value)

    @background
    async def forward(self, value):
        """
        forward(value)

        Move forward by *value*.
        """
        return await self.move(_vectorize(value, 'z'))

    @background
    async def back(self, value):
        """
        back(value)

        Move back by *value*.
        """
        return await self.forward(-value)

    async def _get_position(self):
        """Get the position of the positioner."""
        return await self._get_vector(self.translators)

    async def _set_position(self, position):
        """3D translation to *position*."""
        await self._set_vector(position, self.translators)

    async def _get_orientation(self):
        """Get the angular position of the positioner."""
        return await self._get_vector(self.rotators)

    async def _set_orientation(self, angles):
        """Rotation with magnitudes *angles*."""
        await self._set_vector(angles, self.rotators)

    async def _get_vector(self, axes):
        """Get the current translation or orientation vector."""
        vector = []
        unit = q.m if axes == self.translators else q.rad

        for coordinate in ['x', 'y', 'z']:
            if coordinate in axes:
                vector.append(
                    (await axes[coordinate].get_position()).to(unit).magnitude)
            else:
                vector.append(np.nan)

        return vector * unit

    async def _set_vector(self, vector, axes):
        """Set position (angular or translational) given by *vector* on *axes*."""
        coros = []
        for i, coordinate in enumerate(['x', 'y', 'z']):
            magnitude = vector[i].magnitude
            if not np.isnan(magnitude):
                if coordinate not in axes:
                    if magnitude != 0:
                        # Last chance is to specify the coordinate to be zero.
                        raise PositionerError(
                            'Cannot move in {} coordinate'.format(coordinate))
                else:
                    coro = axes[coordinate].set_position(vector[i])
                    coros.append(coro)

        await asyncio.gather(*coros)
Exemplo n.º 25
0
class DummyDevice(Device):
    """A dummy device."""

    position = Quantity(unit=q.mm)
    sleep_time = Quantity(unit=q.s)
    # Value with a check-decorated setter before it is bound to instance, so still a function
    value = Parameter()
    # value with check wrapping the bound method
    cvalue = Parameter(check=check(source='*', target='standby'))
    # Value set elsewhere
    evalue = Parameter(fset=set_evalue,
                       fget=get_evalue,
                       check=check(source='*', target='standby'))
    slow = Parameter()
    state = State(default='standby')

    def __init__(self, slow=None):
        super(DummyDevice, self).__init__()
        self._position = 1 * q.mm
        self._value = 0
        self._slow = slow
        self._sleep_time = 0.5 * q.s

    async def _get_sleep_time(self):
        return self._sleep_time

    async def _set_sleep_time(self, value):
        self._sleep_time = value

    async def _get_position(self):
        return self._position

    async def _set_position(self, value):
        self._position = value

    async def _get_slow(self):
        try:
            LOG.log(AIODEBUG, 'get slow start %s', self._slow)
            await asyncio.sleep((await self.get_sleep_time()).magnitude)
            LOG.log(AIODEBUG, 'get slow finish %s', self._slow)
            return self._slow
        except asyncio.CancelledError:
            LOG.log(AIODEBUG, 'get slow cancelled %s', self._slow)
            raise
        except KeyboardInterrupt:
            # do not scream
            LOG.debug("KeyboardInterrupt caught while getting")

    async def _set_slow(self, value):
        try:
            LOG.log(AIODEBUG, 'set slow start %s', value)
            await asyncio.sleep((await self.get_sleep_time()).magnitude)
            LOG.log(AIODEBUG, 'set slow finish %s', value)
            self._slow = value
        except asyncio.CancelledError:
            LOG.log(AIODEBUG, 'set slow cancelled %s', value)
            raise

    async def _get_value(self):
        """Get the real value."""
        return self._value

    async def _get_target_value(self):
        """Get the real value."""
        return self._value + 1

    @check(source='standby', target=['standby', 'hard-limit'])
    @transition(immediate='moving', target='standby')
    async def _set_value(self, value):
        """The real value setter."""
        self._value = value

    async def _get_cvalue(self):
        """The real value setter."""
        return self._value

    async def _set_cvalue(self, value):
        """The real value setter."""
        self._value = value

    @background
    async def do_nothing(self, value=None):
        """Do nothing. For testing task canellation."""
        await self._do_nothing(value=value)

    async def _do_nothing(self, value=None):
        LOG.log(AIODEBUG, f'Start doing nothing: {value}')
        try:
            await asyncio.sleep(1)
            LOG.log(AIODEBUG, f'Stop doing nothing: {value}')
            return value
        except asyncio.CancelledError:
            LOG.log(AIODEBUG, f'Doing nothing cancelled: {value}')
            raise

    async def _emergency_stop(self):
        LOG.debug('Emergency stop on a dummy device')
        await asyncio.sleep(0.5)
        self._state_value = 'aborted'
Exemplo n.º 26
0
    def __init__(self, name, params=None):
        """
        Create a new libuca camera.

        The *name* is passed to the uca plugin manager.

        :raises CameraError: In case camera *name* does not exist.
        """

        super(Camera, self).__init__()

        import gi
        gi.require_version('Uca', '2.0')

        from gi.repository import GObject, GLib, Uca

        self._manager = Uca.PluginManager()

        params = params if params else {}

        try:
            self.uca = self._manager.get_camerah(name, params)
        except GLib.GError as ge:
            raise base.CameraError(str(ge))
        except Exception:
            raise base.CameraError("`{0}' is not a valid camera".format(name))

        units = {
            Uca.Unit.METER: q.m,
            Uca.Unit.SECOND: q.s,
            Uca.Unit.DEGREE_CELSIUS: q.celsius,
            Uca.Unit.COUNT: q.dimensionless,
            Uca.Unit.PIXEL: q.pixel,
        }

        parameters = {}

        for prop in self.uca.props:
            if prop.name in ('trigger-source', 'trigger-type',
                             'frames-per-second'):
                continue

            getter, setter, unit = None, None, None

            uca_unit = self.uca.get_unit(prop.name)

            if uca_unit in units:
                unit = units[uca_unit]

            if prop.flags & GObject.ParamFlags.READABLE:
                getter = _new_getter_wrapper(prop.name, unit)

            if prop.flags & GObject.ParamFlags.WRITABLE:
                setter = _new_setter_wrapper(prop.name, unit)

            name = prop.name.replace('-', '_')

            if uca_unit in units:
                parameters[name] = Quantity(unit,
                                            fget=getter,
                                            fset=setter,
                                            help=prop.blurb)
            else:
                parameters[name] = Parameter(fget=getter,
                                             fset=setter,
                                             help=prop.blurb)

        if parameters:
            self.install_parameters(parameters)

        class _Dummy(object):
            pass

        setattr(self.uca, 'enum_values', _Dummy())

        def get_enum_bunch(enum):
            enum_map = {}

            for key, value in list(enum.__enum_values__.items()):
                name = value.value_nick.upper().replace('-', '_')
                enum_map[name] = key

            return Bunch(enum_map)

        for prop in self.uca.props:
            if hasattr(prop, 'enum_class'):
                setattr(self.uca.enum_values, prop.name.replace('-', '_'),
                        get_enum_bunch(prop.default_value))

        self._uca_get_frame_rate = _new_getter_wrapper('frames-per-second')
        self._uca_set_frame_rate = _new_setter_wrapper('frames-per-second')

        # Invert the uca trigger source dict in order to return concert values
        trigger_dict = self.uca.enum_values.trigger_source.__dict__
        self._uca_to_concert_trigger = {
            v: k
            for k, v in list(trigger_dict.items())
        }
        self._uca_get_trigger = _new_getter_wrapper('trigger-source')
        self._uca_set_trigger = _new_setter_wrapper('trigger-source')

        self._record_shape = None
        self._record_dtype = None
Exemplo n.º 27
0
class XRayTube(Device):
    """
    A base x-ray tube class.
    """

    voltage = Quantity(q.kV)
    current = Quantity(q.uA)
    power = Quantity(q.W)

    state = State(default='off')

    def __init__(self):
        super(XRayTube, self).__init__()

    async def _get_state(self):
        raise AccessorNotImplementedError

    async def _get_voltage(self):
        raise AccessorNotImplementedError

    async def _set_voltage(self, voltage):
        raise AccessorNotImplementedError

    async def _get_current(self):
        raise AccessorNotImplementedError

    async def _set_current(self, current):
        raise AccessorNotImplementedError

    async def _get_power(self):
        return (await self.get_voltage() * await self.get_current()).to(q.W)

    @background
    @check(source='off', target='on')
    async def on(self):
        """
        on()

        Enables the x-ray tube.
        """
        await self._on()

    @background
    @check(source='on', target='off')
    async def off(self):
        """
        off()

        Disables the x-ray tube.
        """
        await self._off()

    async def _on(self):
        """
        Implementation of on().
        """
        raise NotImplementedError

    async def _off(self):
        """
        Implementation of off().
        """
        raise NotImplementedError
Exemplo n.º 28
0
class ContinuousSpiralTomography(ContinuousTomography, SpiralMixin):
    """
    Spiral Tomography

    This implements a helical acquisition scheme, where the sample is translated perpendicular to
    the beam while the sample is rotated and the projections are recorded.
    """
    vertical_velocity = Quantity(q.mm / q.s)

    def __init__(self,
                 walker,
                 flat_motor,
                 tomography_motor,
                 vertical_motor,
                 radio_position,
                 flat_position,
                 camera,
                 start_position_vertical,
                 sample_height,
                 vertical_shift_per_tomogram,
                 num_flats=200,
                 num_darks=200,
                 num_projections=3000,
                 angular_range=180 * q.deg,
                 start_angle=0 * q.deg,
                 separate_scans=True):
        """
        :param walker: Walker for storing experiment data.
        :type walker: concert.storage.Walker
        :param flat_motor: LinearMotor for moving sample in and out of the beam.
        :type flat_motor: concert.devices.motors.base.LinearMotor
        :param tomography_motor: ContinuousRotationMotor for tomography scan.
        :type tomography_motor: concert.devices.motors.base.ContinuousLinearMotor
        :param vertical_motor: ContinuousLinearMotor to translate the sample along the tomographic
            axis.
        :type vertical_motor: concert.devices.motors.base.ContinuousLinearMotor
        :param radio_position: Position of *flat_motor* that the sample is positioned in the beam.
        :type radio_position: q.mm
        :param flat_position: Position of *flat_motor* that the sample is positioned out of the
            beam.
        :type flat_position: q.mm
        :param camera: Camera to acquire the images.
        :type camera: concert.devices.cameras.base.Camera
        :param start_position_vertical: Start position of *vertical_motor*.
        :type start_position_vertical: q.mm
        :param sample_height: Height of the sample.
        :type sample_height: q.mm
        :param vertical_shift_per_tomogram: Distance *vertical_motor* is translated during one
            *angular_range*.
        :type vertical_shift_per_tomogram: q.mm
        :param num_flats: Number of images for flatfield correction.
        :type num_flats: int
        :param num_darks: Number of images for dark correction.
        :type num_darks: int
        :param num_projections: Number of projections.
        :type num_projections: int
        :param angular_range: Range for the scan of the *tomography_motor*.
        :type angular_range: q.deg
        :param start_angle: Start position of *tomography_motor* for the first projection.
        :type start_angle: q.deg
        """
        ContinuousTomography.__init__(self,
                                      walker=walker,
                                      flat_motor=flat_motor,
                                      tomography_motor=tomography_motor,
                                      radio_position=radio_position,
                                      flat_position=flat_position,
                                      camera=camera,
                                      num_flats=num_flats,
                                      num_darks=num_darks,
                                      num_projections=num_projections,
                                      angular_range=angular_range,
                                      start_angle=start_angle,
                                      separate_scans=separate_scans)
        SpiralMixin.__init__(
            self,
            vertical_motor=vertical_motor,
            vertical_shift_per_tomogram=vertical_shift_per_tomogram,
            sample_height=sample_height,
            start_position_vertical=start_position_vertical)

    async def _get_vertical_velocity(self):
        shift = await self.get_vertical_shift_per_tomogram()
        fps = await self._camera.get_frame_rate()
        num = await self.get_num_projections()

        return shift * fps / num

    async def _prepare_radios(self):
        """
        Called before the projection images are acquired.

        The :py:meth:`ContinuousTomography._prepare_radios()` is called and the vertical motor is
        moved to vertica_start_position.
        """
        await asyncio.gather(
            ContinuousTomography._prepare_radios(self),
            self._vertical_motor.set_position(
                await self.get_start_position_vertical()))

    async def _finish_radios(self):
        """
        Called after the projection images are required.

        First this stops sample exposure, stops the tomography_motor, stops the vertical_motor.
        Then the *motion_velocity* of tomography_motor and vertical_motor is restored.
        """
        if self._finished:
            return
        await asyncio.gather(self.stop_sample_exposure(),
                             self._tomography_motor.stop(),
                             self._vertical_motor.stop())

        if 'motion_velocity' in self._tomography_motor:
            await self._tomography_motor['motion_velocity'].restore()
        if 'motion_velocity' in self._vertical_motor:
            await self._vertical_motor['motion_velocity'].restore()

        await asyncio.gather(
            self._tomography_motor.set_position(await self.get_start_angle()),
            self._vertical_motor.set_position(
                await self.get_start_position_vertical()))
        self._finished = True

    async def _take_radios(self):
        """
        Generator for the projection images.

        The :py:meth:`_prepare_radios()` is called. Then the *motion_velocity* of the vertical_motor
        is stashed. The velocities of the tomography_motor and the vertical_motor are set-
        """
        try:
            await self._prepare_radios()
            if 'motion_velocity' in self._vertical_motor:
                await self._vertical_motor['motion_velocity'].stash()
            await self._tomography_motor.set_velocity(await
                                                      self.get_velocity())
            await self._vertical_motor.set_velocity(
                await self.get_vertical_velocity())

            num_projections = await self.get_num_projections(
            ) * await self.get_num_tomograms()
            async for frame in self._produce_frames(num_projections):
                yield frame
        finally:
            await self._finish_radios()
Exemplo n.º 29
0
class ContinuousTomography(Tomography):
    """
    Continuous Tomography

    This implements a tomography with a continuous rotation of the sample. The camera must record
    frames with a constant rate.
    """
    velocity = Quantity(q.deg / q.s)
    """Velocity of the *tomography_motor* in the continuous scan."""
    def __init__(self,
                 walker,
                 flat_motor,
                 tomography_motor,
                 radio_position,
                 flat_position,
                 camera,
                 num_flats=200,
                 num_darks=200,
                 num_projections=3000,
                 angular_range=180 * q.deg,
                 start_angle=0 * q.deg,
                 separate_scans=True):
        """
        :param walker: Walker for storing experiment data.
        :type walker: concert.storage.Walker
        :param flat_motor: LinearMotor for moving sample in and out of the beam.
        :type flat_motor: concert.devices.motors.base.LinearMotor
        :param tomography_motor: ContinuousRotationMotor for tomography scan.
        :type tomography_motor: concert.devices.motors.base.ContinuousRotationMotor
        :param radio_position: Position of *flat_motor* that the sample is positioned in the beam.
        :type radio_position: q.mm
        :param flat_position: Position of *flat_motor* that the sample is positioned out of the
            beam.
        :type flat_position: q.mm
        :param camera: Camera to acquire the images.
        :type camera: concert.devices.camera.base.Camera
        :param num_flats: Number of images for flatfield correction.
        :type num_flats: int
        :param num_darks: Number of images for dark correction.
        :type num_darks: int
        :param num_projections: Number of projections.
        :type num_projections: int
        :param angular_range: Range for the scan of the *tomography_motor*.
        :type angular_range: q.deg
        :param start_angle: Start position of *tomography_motor* for the first projection.
        :type start_angle: q.deg
        """
        Tomography.__init__(self,
                            walker=walker,
                            flat_motor=flat_motor,
                            tomography_motor=tomography_motor,
                            radio_position=radio_position,
                            flat_position=flat_position,
                            camera=camera,
                            num_flats=num_flats,
                            num_darks=num_darks,
                            num_projections=num_projections,
                            angular_range=angular_range,
                            start_angle=start_angle,
                            separate_scans=separate_scans)

    async def _get_velocity(self):
        angular_range = await self.get_angular_range()
        num_projections = await self.get_num_projections()
        fps = await self._camera.get_frame_rate()

        return fps * angular_range / num_projections

    async def _prepare_radios(self):
        """
        Called before projection images are acquired.

        Calls :py:meth:`.Tomography._prepare_radios()` and stashed the *motion_velocity* property of
        the *tomography_motor*.
        """
        await super()._prepare_radios()
        if 'motion_velocity' in self._tomography_motor:
            await self._tomography_motor['motion_velocity'].stash()

    async def _finish_radios(self):
        """
        Called after the projections are acquired.

        Stops the continuous motion of the *tomography_motor* and restores the *motion_velocity*.
        Calls :py:meth:`.Tomography._finish_radios()` .
        """
        if self._finished:
            return
        await self._tomography_motor.stop()
        if 'motion_velocity' in self._tomography_motor:
            await self._tomography_motor['motion_velocity'].restore()
        await super()._finish_radios()
        self._finished = True

    async def _take_radios(self):
        """
        Generator for the projection images.

        The :py:meth:`_prepare_radios()` is called. Afterwards the velocity property of the
        tomography_motor is set to correct velocity.
        Then :py:meth:`_produce_frames()` will generate the images.
        At the end :py:meth:`_finish_radios()` is called.
        """
        try:
            await self._prepare_radios()
            await self._tomography_motor.set_velocity(await
                                                      self.get_velocity())

            async for frame in self._produce_frames(
                    await self.get_num_projections()):
                yield frame
        finally:
            await self._finish_radios()
Exemplo n.º 30
0
class Camera(Device):

    """Base class for remotely controllable cameras.

    .. py:attribute:: frame-rate

        Frame rate of acquisition in q.count per time unit.
    """

    trigger_modes = Bunch(['AUTO', 'SOFTWARE', 'EXTERNAL'])
    state = State(default='standby')
    frame_rate = Quantity(1.0 / q.second, help="Frame frequency")
    trigger_mode = Parameter(help="Trigger mode")

    def __init__(self):
        super(Camera, self).__init__()
        self.convert = identity

    @check(source='standby', target='recording')
    def start_recording(self):
        """Start recording frames."""
        self._record_real()

    @check(source='recording', target='standby')
    def stop_recording(self):
        """Stop recording frames."""
        self._stop_real()

    @contextlib.contextmanager
    def recording(self):
        """
        A context manager for starting and stopping the camera.

        In general it is used with the ``with`` keyword like this::

            with camera.recording():
                frame = camera.grab()
        """
        self.start_recording()
        try:
            yield
        finally:
            self.stop_recording()

    def trigger(self):
        """Trigger a frame if possible."""
        self._trigger_real()

    def grab(self):
        """Return a NumPy array with data of the current frame."""
        return self.convert(self._grab_real())

    @async
    def grab_async(self):
        return self.grab()

    @async
    def stream(self, consumer):
        """Grab frames continuously and send them to *consumer*, which
        is a coroutine.
        """
        self.trigger_mode = self.trigger_modes.AUTO
        self.start_recording()

        while self.state == 'recording':
            consumer.send(self.grab())

    def _get_trigger_mode(self):
        raise AccessorNotImplementedError

    def _set_trigger_mode(self, mode):
        raise AccessorNotImplementedError

    def _record_real(self):
        raise AccessorNotImplementedError

    def _stop_real(self):
        raise AccessorNotImplementedError

    def _trigger_real(self):
        raise AccessorNotImplementedError

    def _grab_real(self):
        raise AccessorNotImplementedError