Exemplo n.º 1
0
class VectorCoil(PowerSupply):
    """VectorCoil is a device to control a coil which creates a field at the
    sample position.

    Basically it is a powersupply device, working in Amps and having two
    additional parameters for calibration the vectorfield, for which these
    coil devices are used.
    """

    parameters = {
        'orientation':
        Param(
            'Field vector which is created by this coil in '
            'mT (measured value!)',
            settable=True,
            default=(1., 1., 1.),
            type=tupleof(float, float, float),
            unit='mT',
            category='general'),
        'calibrationcurrent':
        Param(
            'Current in A which created the field '
            'given as Parameter orientation',
            settable=True,
            default=1.,
            type=float,
            unit='A',
            category='general'),
    }
Exemplo n.º 2
0
class DetectorMixin(DeviceMixinBase):
    """Provide the size parameter."""
    parameters = {
        'size':
        Param('size of the active detector area',
              type=tupleof(floatrange(0), floatrange(0)),
              settable=False,
              mandatory=False,
              unit='mm',
              default=(1000., 1000.)),
    }
Exemplo n.º 3
0
class NOKMonitoredVoltage(Sensor):
    """Return a scaled and monitored Analogue value.

    Also checks the value to be within certain limits, if not, complain.
    """

    parameters = {
        'reflimits':
        Param('None or Limits to check the reference: low, warn, '
              'high',
              type=none_or(tupleof(float, float, float)),
              settable=False,
              default=None),
        'scale':
        Param('Scaling factor', type=float, settable=False, default=1.),
    }
    parameter_overrides = {
        'unit': Override(default='V', mandatory=False),
    }

    def doInit(self, mode):
        if self.reflimits is not None:
            if not (0 <= self.reflimits[0] <= self.reflimits[1] <=
                    self.reflimits[2]):
                raise ConfigurationError(
                    self, 'reflimits must be in ascending'
                    ' order!')

    def doRead(self, maxage=0):
        value = self.scale * Sensor.doRead(self, maxage)
        if self.reflimits is not None:
            if abs(value) > self.reflimits[2]:
                raise HardwareError(
                    self, 'Reference voltage (%.2f) above '
                    'maximum (%.2f)' % (value, self.reflimits[2]))
            if abs(value) < self.reflimits[0]:
                raise HardwareError(
                    self, 'Reference voltage (%.2f) below '
                    'minimum (%.2f)' % (value, self.reflimits[0]))
            if abs(value) < self.reflimits[1]:
                self.log.warning(
                    'Reference voltage (%.2f) seems rather low, '
                    'should be above %.2f', value, self.reflimits[1])
        return value

    def doStatus(self, maxage=0):
        try:
            self.doRead(maxage)
            return Sensor.doStatus(self, maxage)
        except HardwareError as err:
            return status.ERROR, repr(err)
Exemplo n.º 4
0
def test_tupleof():
    assert tupleof(int, str, float)((1.0, 1.0, 1.0)) == (1, '1.0', 1.0)
    assert tupleof(int, str, float)() == (0, '', 0.0)
    assert tupleof(float, float)() == (0.0, 0.0)
    assert tupleof(int, int, int, int)(array((1, 2, 3, 4))) == (1, 2, 3, 4)
    assert raises(ValueError, tupleof(int, str), ('a', 'b'))
    assert raises(ValueError, tupleof(int, str), ('a', ))
    assert raises(ValueError, tupleof(int, str), 'x')
    assert raises(
        ProgrammingError,
        tupleof,
    )
Exemplo n.º 5
0
class HexapodSpecial(PyTangoDevice, Device):
    """Ohe Hexapod Device for Workspace Configuration."""

    parameters = {
        "workspaces": Param("Hexapod workspaces list containing tuples of "
                            "(id, [xn, xp, yn, yp, zn, zp, rzn, rzp, ryn, ryp, "
                            "rxn, rxp, tx, ty, tz, rz, ry, rx])",
                            type=listof(tupleof(int, listof(float))),
                            mandatory=True, settable=False)
    }

    def doInit(self, mode):
        if any(idx < 10 or idx > 19 for idx, _ in self.workspaces):
            raise ConfigurationError("Workspace ids muste be in 10..19 "
                                     "(Jülich workspace range)")
        if mode != SIMULATION:
            workspaces = self._dev.workspaces  # Tango get workspaces
            for wsid, ws in self.workspaces:
                workspaces[wsid] = ws
            self._dev.workspaces = workspaces  # Tango set workspaces
Exemplo n.º 6
0
class CalibratedMagnet(HasLimits, Moveable):
    """Base clase for magnet supplies having an bipolar current source.

    Use this for magnets which can be calibrated, i.e. where::

        B(I) = Ic0 + c1*erf(c2*I) + c3*atan(c4*I)

    works reasonably well.
    Coefficients c0..c4 are given as 'calibration' parameter.
    """

    attached_devices = {
        'currentsource': Attach('bipolar Powersupply', Moveable),
    }

    parameters = {
        'ramp':
        Param('Target rate of field change per minute',
              unit='main/min',
              mandatory=False,
              settable=True,
              volatile=True),
        'calibration':
        Param(
            'Coefficients for calibration '
            'function: [c0, c1, c2, c3, c4] calculates '
            'B(I) = c0*I + c1*erf(c2*I) + c3*atan(c4*I)'
            ' in T',
            type=tupleof(float, float, float, float, float),
            default=(1.0, 0.0, 0.0, 0.0, 0.0),
            settable=True,
            chatty=True),
    }

    parameter_overrides = {
        'unit': Override(mandatory=False, default='T'),
        'abslimits': Override(mandatory=False, volatile=True),
    }

    def _current2field(self, current, *coefficients):
        """Return field in T for given current in A.

        Should be monotonic and asymetric or _field2current will fail!

        Note: This may be overridden in derived classes.
        """
        v = coefficients or self.calibration
        if len(v) != 5:
            self.log.warning('Wrong number of coefficients in calibration '
                             'data!  Need exactly 5 coefficients!')
        return v[0]*current + v[1]*math.erf(v[2]*current) + \
            v[3]*math.atan(v[4]*current)

    def _field2current(self, field):
        """Return required current in A for requested field in T.

        Note: This may be overridden in derived classes.
        """
        # binary search/bisection
        maxcurr = self._attached_currentsource.abslimits[1]
        mincurr = -maxcurr
        maxfield = self._current2field(maxcurr)
        minfield = self._current2field(mincurr)
        if not minfield <= field <= maxfield:
            raise LimitError(
                self, 'requested field %g %s out of range %g..%g %s' %
                (field, self.unit, minfield, maxfield, self.unit))

        res = fsolve(lambda cur: self._current2field(cur) - field, 0)[0]
        self.log.debug('current for %g %s is %g', field, self.unit, res)
        return res

    def doRead(self, maxage=0):
        return self._current2field(self._attached_currentsource.read(maxage))

    def doStart(self, target):
        self._attached_currentsource.start(self._field2current(target))

    def doStop(self):
        self._attached_currentsource.stop()

    def doReset(self):
        return self._attached_currentsource.reset()

    def doReadRamp(self):
        # This is an approximation!
        return self.calibration[0] * abs(self._attached_currentsource.ramp)

    def doWriteRamp(self, newramp):
        # This is an approximation!
        self._attached_currentsource.ramp = newramp / self.calibration[0]

    def doReadAbslimits(self):
        minfield, maxfield = [
            self._current2field(I)
            for I in self._attached_currentsource.abslimits
        ]
        # include 0 in allowed range
        if minfield > 0:
            minfield = 0
        if maxfield < 0:
            maxfield = 0
        # get configured limits if any, or take max from source
        limits = self._config.get('abslimits', (minfield, maxfield))
        # in any way, clamp limits to what the source can handle
        limits = [clamp(i, minfield, maxfield) for i in limits]
        return min(limits), max(limits)

    def doWriteUserlimits(self, limits):
        HasLimits.doWriteUserlimits(self, limits)
        # all Ok, set source to max of pos/neg field current
        maxcurr = max(self._field2current(i) for i in limits)
        mincurr = min(self._field2current(i) for i in limits)
        self._attached_currentsource.userlimits = (mincurr, maxcurr)

    def doTime(self, startval, target):
        # get difference in current
        delta = abs(
            self._field2current(target) - self._field2current(startval))
        # ramp is per minute, doTime should return seconds
        return 60 * delta / self._attached_currentsource.ramp

    @usermethod
    def calibrate(self, fieldcolumn, *scannumbers):
        """Calibrate the B to I conversion, argument is one or more scan numbers.

        The first argument specifies the name of the device which should
        be used as a measuring device for the field.

        Example:

        >>> B_mira.calibrate(Bf, 351)
        """
        scans = session.experiment.data.getLastScans()
        self.log.info('determining calibration from scans, please wait...')
        Is = []
        Bs = []
        currentcolumn = self._attached_currentsource.name
        # XXX(dataapi): adapt to new Dataset class
        for scan in scans:
            if scan.counter not in scannumbers:
                continue
            if fieldcolumn not in scan.ynames or \
                    currentcolumn not in scan.xnames:
                self.log.info('%s is not a calibration scan', scan.counter)
                continue
            xindex = scan.xnames.index(currentcolumn)
            yindex = scan.ynames.index(fieldcolumn)
            yunit = scan.yunits[yindex]
            if yunit == 'T':
                factor = 1.0
            elif yunit == 'mT':
                factor = 1e-3
            elif yunit == 'uT':
                factor = 1e-6
            elif yunit == 'G':
                factor = 1e-4
            elif yunit == 'kG':
                factor = 1e-1
            else:
                raise NicosError(
                    self, 'unknown unit for B field '
                    'readout: %r' % yunit)
            for xr, yr in zip(scan.xresults, scan.yresults):
                Is.append(xr[xindex])
                Bs.append(yr[yindex] * factor)
        if not Is:
            self.log.error('no calibration data found')
            return
        fit = Fit('calibration', self._current2field,
                  ['c%d' % i for i in range(len(self.calibration))],
                  [1] * len(self.calibration))
        res = fit.run(Is, Bs, [1] * len(Bs))
        if res._failed:
            self.log.warning('fit failed')
            return
        self.calibration = res._pars[1]
Exemplo n.º 7
0
class AndorHFRCamera(PyTangoDevice, UsesFastshutter, ImageChannelMixin,
                     ActiveChannel):
    """Camera communicating with Andor Basic script."""

    TRIGGER_MODES = {
        'internal': 0,
        'external': 1,
        'external start': 6,
        'external exposure': 7,
        'external exposure FVB': 9,
    }

    SHUTTER_MODES = {
        'rolling': 0,
        'global': 1,
    }

    parameters = {
        'bin':
        Param('Binning (x,y)',
              type=tupleof(intrange(1, 64), intrange(1, 64)),
              settable=True,
              default=(1, 1),
              category='general'),
        'triggermode':
        Param('Triggering signal definition',
              type=oneof(*TRIGGER_MODES),
              settable=True,
              default='internal',
              category='general'),
        'shuttermode':
        Param('Shutter mode setting',
              type=oneof(*SHUTTER_MODES),
              settable=True,
              default='rolling',
              category='general'),
        'spooldirectory':
        Param('Path to spool the images on the remote '
              'machine',
              type=absolute_win_path,
              category='general'),
        'extratime':
        Param('Extra sleeping time to sync with Andors Basic',
              type=floatrange(0),
              default=3,
              settable=True),
        '_started':
        Param('Cached counting start flag',
              type=none_or(float),
              default=None,
              settable=True,
              internal=True),
    }

    def doPreinit(self, mode):
        PyTangoDevice.doPreinit(self, mode)
        self.log.info('Checking if camera script is ready!')
        try:
            msg = self._dev.communicate('ready?')
            if msg.strip() != 'ready!':
                raise CommunicationError(
                    self, 'Camera script gave wrong '
                    'answer - please check!')
        except NicosError:
            self.log.warning('Camera is not responding - please start '
                             'tomography script on camera first!')
            raise

    def doReset(self):
        self.log.info('Checking if camera script is ready!')
        try:
            msg = self._dev.communicate('ready?')
            if msg.strip() != 'ready!':
                raise CommunicationError(
                    self, 'Camera script gave wrong '
                    'answer - please check!')
        except NicosError:
            self.log.warning('Camera is not responding - please start '
                             'tomography script on camera first!')
            raise

    def doInit(self, mode):
        self._started = None

    def valueInfo(self):
        # no readresult by default
        return ()

    def doStart(self):
        self.bin = self.bin
        self.shuttermode = self.shuttermode
        self.triggermode = self.triggermode
        self.doWriteSpooldirectory(self.spooldirectory)
        kct = float(self._query_value('GetKineticCycleTime'))
        self.log.info('kct: %s', kct)
        self._counttime = self._knumber * kct + 3
        self.log.info('measure time = %s s', self._counttime)
        self.openFastshutter()
        self._write_command('count')
        self._started = currenttime()
        self.log.debug('started: %s', self._started)

    def doSetPreset(self, **presets):
        for preset, val in presets.items():
            self._write_presets(preset, val)
        self._write_presets(
            'spoolfile',
            presets.get('spoolfile', session.experiment.sample.samplename))

    def presetInfo(self):
        return ['exptime', 'number', 'cycletime', 'spoolfile']

    def doStatus(self, maxage=0):
        if self._started:
            if (self._started + self._counttime) > currenttime():
                return status.BUSY, 'counting'
        return status.OK, 'idle'

    def doStop(self):
        self._started = None
        self.status(0)
        self._attached_fastshutter.maw('closed')

    def doFinish(self):
        self._started = None
        # self._attached_fastshutter.maw('closed')

    def doWriteTriggermode(self, value):
        self._write_command('SetTriggerMode %d' % self.TRIGGER_MODES[value])

    def doWriteShuttermode(self, value):
        self._write_command('SetShutterMode %d' % self.SHUTTER_MODES[value])

    def doWriteSpooldirectory(self, value):
        self._write_command('SetSpoolDirectory %s' % value)

    def doWriteBin(self, value):
        self._write_command('SetHBin %d' % value[0])
        self._write_command('SetVBin %d' % value[1])

    def _write_command(self, command):
        ret = self._dev.communicate(command)
        if not ret.startswith('ACK'):
            if ret.startswith('ERR'):
                raise InvalidValueError(self,
                                        'Command: %s is invalid ' % command)
            raise InvalidValueError(
                self, 'Command: %s has invalid '
                'parameters' % command)

    def _query_value(self, request):
        return self._dev.communicate(request)

    def _write_presets(self, preset, value):
        if preset == 'exptime':
            self._write_command('SetExposureTime %f' % value)
        elif preset == 'number':
            self._write_command('SetKineticNumber %d' % value)
            self._knumber = int(value)
        elif preset == 'cycletime':
            self._write_command('SetKineticCycleTime %f' % value)
        elif preset == 'spoolfile':
            self._write_command('SetSpoolFileName %s' % value)
Exemplo n.º 8
0
class GuideField(MappedMoveable):
    """Guidefield object.

    Needs to be switched to (re-)calculate the required currents for the coils.
    Calibration is done with a matrix giving the field components created with
    a given current.

    Needs the alpha virtual motor for calculations.
    """
    attached_devices = {
        'alpha':
        Attach('Device which provides the current \\alpha', AlphaStorage),
        'coils':
        Attach('List of 3 devices used for the vector field',
               VectorCoil,
               multiple=3),
    }
    parameter_overrides = {
        'mapping':
        Override(mandatory=False,
                 type=dict,
                 default={
                     'off': None,
                     'perp': (1., 0., 0.),
                     '-perp': (-1., 0., 0.),
                     'par': (0., 1., 0.),
                     '-par': (0., -1., 0.),
                     'z': (0., 0., 1.),
                     '-z': (0., 0., -1.),
                     'up': (0., 0., 1.),
                     'down': (0., 0., -1.),
                     '0': (0., 0., 0.),
                 }),
        'precision':
        Override(mandatory=False),
    }
    parameters = {
        'background':
        Param(
            'Static magnetic field which is always present and'
            ' should be corrected',
            type=tupleof(float, float, float),
            unit='mT',
            settable='True',
            default=(0., 0., 0.),
            category='general'),
        'field':
        Param('Absolute value of the desired field at the '
              'sample position',
              type=floatrange(0.1, 100),
              unit='mT',
              settable='True',
              default=25.,
              category='general'),
    }

    _currentmatrix = None
    _currentmatrix_inv = None

    @lazy_property
    def coils(self):
        return self._attached_coils

    @lazy_property
    def alpha(self):
        return self._attached_alpha

    def doInit(self, mode):
        MappedMoveable.doInit(self, mode)
        M = np.zeros([3, 3])
        for i in range(3):
            for j in range(3):
                M[i, j] = (self.coils[j].orientation[i] /
                           self.coils[j].calibrationcurrent)
        self._currentmatrix = M
        self._currentmatrix_inv = np.linalg.inv(M)
        self.alpha._callback = self._alphaCallBack

    def doShutdown(self):
        self.alpha._callback = None

    def _alphaCallBack(self):
        if self.target in self.mapping:
            self.doStart(self.target)

    def _startRaw(self, orient):
        if orient:
            orient = np.array(orient)
            # set requested field (may try to compensate background)
            self._setfield(self.field * orient)
        else:  # switch off completely
            self.coils[0].doStart(0.0)
            self.coils[1].doStart(0.0)
            self.coils[2].doStart(0.0)

    # no _readRaw, as self.target is the unmapped (Higher level) value
    def doRead(self, maxage=0):
        return self.target

    def _B2I(self, B=np.array([0.0, 0.0, 0.0])):
        r"""Rotate the requested field around z-axis by beta.

        First we get alpha from the spectrometer: alpha is the angle between
        X-axis and \\vec{Q} and is in degrees.
        """
        # read alpha, calculate beta
        alpha = self.alpha.read(0)
        beta = np.radians(90 - alpha)
        R = np.array([[np.cos(beta), -np.sin(beta), 0.0],
                      [np.sin(beta), np.cos(beta), 0.0], [0.0, 0.0, 1.0]])
        temp = np.dot(self._currentmatrix_inv, np.dot(R, B))
        return temp

    def _I2B(self, I=np.array([0.0, 0.0, 0.0])):
        """Calculate field from currents and rotate field around z-axis by
        -beta.
        """
        # read alpha, calculate beta
        alpha = self.alpha.read(0)
        beta = np.radians(90 - alpha)
        RR = np.array([[np.cos(beta), -np.sin(beta), 0.0],
                       [np.sin(beta), np.cos(beta), 0.0], [0.0, 0.0, 1.0]])
        return np.dot(RR, np.dot(self._currentmatrix, I))

    def _setfield(self, B=np.array([0, 0, 0])):
        r"""Set the given field.

        Field components are:

        * Bqperp: component perpendicular to q, but within the scattering plane
        * Bqpar:  component parallel to q (within scattering plane)
        * Bz:     component perpendicular to the scattering plane

        (If TwoTheta==0 & \\hbar\\omega=0 then this coordinate-system is the
        same as the XYZ of the coils.)
        """
        # subtract offset (The field, which is already there, doesn't need to
        # be generated....)
        B = B - np.array(self.background)
        F = self._B2I(B)  # compute currents for requested field

        # now check limits
        valueOk = True
        for i, d in enumerate(self.coils):
            check = d.isAllowed(F[i])
            if not check[0]:
                self.log.error('Can\'t set %s to %s: %s', d,
                               d.format(F[i], unit=True), check[1])
                valueOk = False

        if not valueOk:
            raise LimitError(check[1])

        # go there
        for i, d in enumerate(self.coils):
            d.start(F[i])
Exemplo n.º 9
0
class CascadeDetector(VirtualImage):

    _perfoil = 16

    parameters = {
        'mode':
        Param('Data acquisition mode (tof or image)',
              type=oneof('tof', 'image'),
              settable=True,
              default='image',
              category='presets'),
        'roi':
        Param('Region of interest, given as (x1, y1, x2, y2)',
              type=tupleof(int, int, int, int),
              default=(-1, -1, -1, -1),
              settable=True),
        'tofchannels':
        Param('Total number of TOF channels to use',
              type=intrange(1, 1024),
              default=128,
              settable=True,
              category='presets'),
        'foilsorder':
        Param('Usable foils, ordered by number.',
              type=listof(intrange(0, 31)),
              settable=False,
              default=[0, 1, 2, 3, 4, 5, 6, 7],
              category='instrument'),
        'fitfoil':
        Param('Foil for contrast fitting (number BEFORE '
              'resorting)',
              type=int,
              default=0,
              settable=True),
    }

    def doInit(self, mode):
        self._buf = self._generate(0).astype('<u4')

    def doUpdateMode(self, value):
        self._dataprefix = (value == 'image') and 'IMAG' or 'DATA'
        self._datashape = (value == 'image') and self.sizes or \
            (self._perfoil * len(self.foilsorder), ) + self.sizes
        # (self.tofchannels,)
        self._tres = (value == 'image') and 1 or self.tofchannels

    def doStart(self):
        self.readresult = [0, 0]
        VirtualImage.doStart(self)

    def valueInfo(self):
        if self.mode == 'tof':
            return (Value(self.name + '.roi',
                          unit='cts',
                          type='counter',
                          errors='sqrt',
                          fmtstr='%d'),
                    Value(self.name + '.total',
                          unit='cts',
                          type='counter',
                          errors='sqrt',
                          fmtstr='%d'),
                    Value('fit.contrast',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.3f'),
                    Value('fit.contrastErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.3f'),
                    Value('fit.avg',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.1f'),
                    Value('fit.avgErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.1f'),
                    Value('roi.contrast',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.3f'),
                    Value('roi.contrastErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.3f'),
                    Value('roi.avg',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.1f'),
                    Value('roi.avgErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.1f'))
        return (Value(self.name + '.roi',
                      unit='cts',
                      type='counter',
                      errors='sqrt',
                      fmtstr='%d'),
                Value(self.name + '.total',
                      unit='cts',
                      type='counter',
                      errors='sqrt',
                      fmtstr='%d'))

    @property
    def arraydesc(self):
        if self.mode == 'image':
            return ArrayDesc('data', self._datashape, '<u4', ['X', 'Y'])
        return ArrayDesc('data', self._datashape, '<u4', ['T', 'X', 'Y'])

    def doReadArray(self, quality):
        # get current data array from detector, reshape properly
        data = VirtualImage.doReadArray(self, quality)
        # determine total and roi counts
        total = data.sum()
        if self.roi != (-1, -1, -1, -1):
            x1, y1, x2, y2 = self.roi
            roi = data[..., y1:y2, x1:x2].sum()
        else:
            x1, y1, x2, y2 = 0, 0, data.shape[-1], data.shape[-2]
            roi = total

        if self.mode == 'image':
            self.readresult = [roi, total]
            return data

        data = np.array([data] * self._datashape[0])

        # demux timing into foil + timing
        nperfoil = self._datashape[0] // len(self.foilsorder)
        shaped = data.reshape((len(self.foilsorder), nperfoil) +
                              self._datashape[1:])
        # nperfoil = self.tofchannels // self.foils
        # shaped = data.reshape((self.foils, nperfoil) + self._datashape[1:])

        x = np.arange(nperfoil)

        ty = shaped[self.fitfoil].sum((1, 2))
        ry = shaped[self.fitfoil, :, y1:y2, x1:x2].sum((1, 2))

        self.log.debug('fitting %r and %r' % (ty, ry))

        self.readresult = [
            roi,
            total,
        ]

        # also fit per foil data and pack everything together to be send
        # via cache for display
        payload = []
        for foil in self.foilsorder:
            foil_tot = shaped[foil].sum((1, 2))
            foil_roi = shaped[foil, :, y1:y2, x1:x2].sum((1, 2))
            tpopt, tperr, _ = fit_a_sin_fixed_freq(x, foil_tot)
            rpopt, rperr, _ = fit_a_sin_fixed_freq(x, foil_roi)
            payload.append([
                tpopt, tperr,
                foil_tot.tolist(), rpopt, rperr,
                foil_roi.tolist()
            ])

        self._cache.put(self.name, '_foildata', payload, flag=FLAG_NO_STORE)
        return data
Exemplo n.º 10
0
class DoubleMotorBeckhoffNOK(DoubleMotorBeckhoff):
    """NOK using two axes.
    """

    parameters = {
        'mode':
        Param('Beam mode',
              type=oneof(*MODES),
              settable=True,
              userparam=True,
              default='ng',
              category='experiment'),
        'nok_motor':
        Param('Position of the motor for this NOK',
              type=tupleof(float, float),
              settable=False,
              unit='mm',
              category='general'),
        'inclinationlimits':
        Param('Allowed range for the positional '
              'difference',
              type=limits,
              mandatory=True),
        'backlash':
        Param('Backlash correction in phys. units',
              type=float,
              default=0.,
              unit='main'),
        'offsets':
        Param('Offsets of NOK-Motors (reactor side, sample side)',
              type=tupleof(float, float),
              default=(0., 0.),
              settable=False,
              unit='main',
              category='offsets'),
    }

    parameter_overrides = {
        'precision':
        Override(type=floatrange(0, 100)),
        'masks':
        Override(type=dictwith(**{name: float
                                  for name in MODES}),
                 unit='',
                 mandatory=True),
        'address':
        Override(mandatory=False),
    }

    valuetype = tupleof(float, float)
    _honor_stop = True

    def doInit(self, mode):
        for name, idx in [('reactor', 0), ('sample', 1)]:
            self.__dict__[name] = SingleMotorOfADoubleMotorNOK(
                '%s.%s' % (self.name, name),
                unit=self.unit,
                both=self,
                lowlevel=True,
                index=idx)

    def doWriteMode(self, mode):
        self.log.debug('DoubleMotorBeckhoffNOK arg:%s  self:%s', mode,
                       self.mode)
        target = self.doRead(0)
        self.log.debug('DoubleMotorBeckhoffNOK target %s', target)
        target = [pos + self.masks[mode] for pos in target]
        self.log.debug('DoubleMotorBeckhoffNOK target %s', target)
        DoubleMotorBeckhoff.doStart(self, target)

    def doRead(self, maxage=0):
        self.log.debug('DoubleMotorBeckhoffNOK read')
        return [
            pos - self.masks[self.mode]
            for pos in DoubleMotorBeckhoff.doRead(self, maxage)
        ]

    def doIsAllowed(self, target):
        self.log.debug('DoubleMotorBeckhoffNOK doIsAllowed')
        target_r, target_s = (target[0] + self.offsets[0],
                              target[1] + self.offsets[1])

        incmin, incmax = self.inclinationlimits

        inclination = target_s - target_r
        if not incmin < inclination < incmax:
            return False, 'Inclination %.2f out of limit (%.2f, %.2f)!' % (
                inclination, incmin, incmax)

        # no problems detected, so it should be safe to go there....
        return True, ''

    def doIsAtTarget(self, pos, target):
        self.log.debug('DoubleMotorBeckhoffNOK doIsAtTarget')
        stat = self.status(0)
        if stat[0] == status.BUSY:
            return False
        return True

    def doStart(self, target):
        self.log.debug('DoubleMotorBeckhoffNOK doStart')
        self.log.debug('target %s %s', target, type(target))
        target = [pos + self.masks[self.mode] for pos in target]
        self.log.debug('target %s %s', target, type(target))
        DoubleMotorBeckhoff.doStart(self, target)
Exemplo n.º 11
0
class DetAngle(HasLimits, Moveable):
    """Angle of the outgoing (centered) beam to detector."""

    attached_devices = {
        'tubeangle': Attach('Angle of the tube to the ground', Moveable),
        'tubepos': Attach('Position of detector inside tube', Readable),
        'pivot': Attach('Position of the pivot point', Readable),
        'theta': Attach('Tilt angle of the sample', Readable, optional=True),
    }

    parameters = {
        'pivot1pos':
        Param('Distance of the pivot point 1 from wall',
              type=floatrange(0),
              mandatory=False,
              default=290,
              unit='mm'),
        'b2pos':
        Param('Distance of B2 aperture from wall',
              type=floatrange(0),
              mandatory=False,
              default=165,
              unit='mm'),
        'b3pos':
        Param('Distance of B3 aperture from wall',
              type=floatrange(0),
              mandatory=False,
              default=285,
              unit='mm'),
        'samplepos':
        Param('Distance of sample from B3',
              type=floatrange(0),
              mandatory=False,
              default=50,
              unit='mm'),
        'detheight':
        Param('Height of the detector',
              type=floatrange(0),
              mandatory=False,
              default=533.715,
              unit='mm'),
        # calculated from the beamheight - pivot.height - (256 - 160) * 2.093 mm
        # Beam height at 0 deg is in pixel 160 counted from top pixel (256)
        # times pixel size (2.093 mm)
        'detoffset':
        Param('Offset from virtual tube base to lower edge of '
              'detector',
              type=floatrange(0),
              mandatory=False,
              default=619.772,
              unit='mm'),
        'beamheight':
        Param('Height of beam above ground level',
              type=floatrange(0),
              mandatory=False,
              default=1193.7,
              unit='mm'),
        'range':
        Param('range of angles between upper and lower detector edge',
              type=tupleof(float, float),
              volatile=True,
              unit='deg, deg',
              internal=True,
              preinit=False),
    }

    parameter_overrides = {
        'unit': Override(mandatory=False, default='deg'),
        'abslimits': Override(mandatory=False, volatile=True),
    }

    def doPreinit(self, mode):
        # center of the detector in respect to the tube base line
        self._detcenter = self.detheight / 2 + self.detoffset
        self._yoffset = self.beamheight - self._attached_pivot.height

    def doInit(self, mode):
        self._func = np.vectorize(self._alpha)

    def doRead(self, maxage=0):
        self._update(maxage)
        beta = self._attached_tubeangle.read(maxage)
        self.log.debug('Tube angle: %.3f', beta)
        alphaf = self._alpha(beta)
        if self._attached_theta:
            alphaf -= self._attached_theta.read(maxage)
        return alphaf

    def _update(self, maxage=0):
        pivot = self._attached_pivot
        # Calculate the distance between sample and pivot point
        self._xoffset = self.pivot1pos + pivot.grid * (
            pivot.read(maxage) - 1) - (self.samplepos + self.b3pos)
        self.log.debug('Sample pivot distance : %.1f', self._xoffset)
        self._tpos = self._attached_tubepos.read(maxage)

    def _alpha(self, beta):
        beta = radians(beta)
        # calculate the detector center position in respect to sample
        x = self._xoffset + self._tpos * cos(beta) - self._detcenter * sin(
            beta)

        # calculate the height of the detector center in respect to the ground
        y = self._tpos * sin(beta) + self._detcenter * cos(
            beta) - self._yoffset
        return degrees(atan2(y, x))

    def doStart(self, target):
        self._update(0)
        x = np.arange(self.absmin, self.absmax + 0.01, 0.01)
        y = self._func(x)
        if self._attached_theta:
            target += self._attached_theta.read(0)
        val = np.interp(target, y, x)
        self.log.debug('new beta: %f', val)
        self._attached_tubeangle.start(val)

    def doReadAbslimits(self):
        return self._attached_tubeangle.abslimits

    def doReadRange(self):
        alpha = self.doRead(0)
        opening = degrees(atan2(self.detheight, self._tpos)) / 2.
        return (alpha - opening, alpha + opening)
Exemplo n.º 12
0
class HasTimeout(DeviceMixinBase):
    """
    Mixin class for devices whose wait() should have a simple timeout.

    Classes using this mixin may provide a `timeoutAction` which gets
    called once as soon as the device times out (and a status is obtained).
    Any Exceptions occurring therein will not stop a script!

    The time at which the device will get a timeout (in case it is still in
    `status.BUSY` or has not reached its target value which corresponds to
    `isAtTarget` being False if implemented) is determined upon calling
    `start` and is saved to the non-userparameter `_timesout`.
    If you need a more dynamic timeout calculation, you may provide a
    `doReadTimeout` method (with volatile=True) to calculate the extra amount
    on the fly.

    The relevant timestamps are internally stored in the (non-userparam)
    `_timesout`, which is either set to None, if there is no nicos-initiated
    movement, or to an iterable of tuples describing what action is supposedly
    performed and at which time it should be finished.
    If there is a doTime method, it is used to calculate the length of an
    intermediary 'ramping' phase and timeout. This may be followed by other
    phases. If `timeout` is not None, a last phase is added which takes
    `timeout` seconds to time out.

    You may set the `timeout` parameter to None in which case the device will
    never time out.
    """
    parameters = {
        'timeout':
        Param('Time limit for the device to reach its target'
              ', or None',
              unit='s',
              fmtstr='%.1f',
              type=none_or(float),
              settable=True,
              mandatory=False,
              chatty=True),
        '_timesout':
        Param('Device movement should finish between these '
              'timestamps',
              type=none_or(nonemptylistof(tupleof(string, float))),
              unit='s',
              userparam=False,
              settable=True),
    }

    # derived classes may redefine this if they need different behaviour
    timeout_status = status.NOTREACHED

    # internal flag to determine if the timeout action has been executed
    _timeoutActionCalled = False

    @property
    def _startTime(self):
        # only read the parameter once from the cache db to avoid race
        # condition between the checks below
        timesout = self._timesout
        return timesout[0][1] if timesout else None

    @property
    def _timeoutTime(self):
        # see above
        timesout = self._timesout
        return timesout[-1][1] if timesout else None

    def _getTimeoutTimes(self, current_pos, target_pos, current_time):
        """Calculates timestamps for timeouts

        returns an iterable of tuples (status_string, timestamp) with ascending
        timestamps.
        First timestamp has to be `current_time` which is the only argument to
        this.
        The last timestamp will be used as the final timestamp to determine if
        the device's movement timed out or not.
        Additional timestamps (in between) may be set if need for
        _combinedStatus returning individual status text's (e.g. to
        differentiate between 'ramping' and 'stabilization').
        """
        res = [('start', current_time)]
        if hasattr(self, 'doTime'):
            res.append(('', res[-1][1] + self.doTime(current_pos, target_pos)))
        if self.timeout:
            res.append(('', res[-1][1] + self.timeout))
        return res

    def isTimedOut(self):
        """Method to (only) check whether a device's movement timed out or not.

        Returns False unless there was a timeout in which case it returns True.
        """
        if self.timeout is None:
            return False
        timeoutTime = self._timeoutTime
        if timeoutTime is not None:
            remaining = timeoutTime - currenttime()
            if remaining > 0:
                self.log.debug("%.2f s left before timeout", remaining)
            else:
                self.log.debug("timeout since %.2f s", -remaining)
            return remaining < 0
        return False

    def resetTimeout(self, target):
        """Method called to reset the timeout when the device is started to
        a new target.
        """
        self._timeoutActionCalled = False
        timesout = self._getTimeoutTimes(self.read(), target, currenttime())
        self._setROParam('_timesout', timesout)

    def _clearTimeout(self):
        self._setROParam('_timesout', None)

    def _targetReached(self):
        """Clears timeout in order to suppress further timeouts after the
        device has reached its target. This is determined by `_combinedStatus`.
        Thus this will just be checked when the status is polled periodically
        before the device timed out.
        This behaviour may be changed in derived classes,
        e.g. `HasWindowTimeout`.
        """
        self._clearTimeout()

    def _combinedStatus(self, maxage=0):
        """Create a combined status from doStatus, isAtTarget and timedOut

        If a timeout happens, use the status set by self.timeout_status and
        call `timeoutAction` once if defined. Pollers and other `SLAVE`s do
        *not* call `timeoutAction`.
        """
        from nicos.core.device import Readable  # isort:skip
        code, msg = Readable._combinedStatus(self, maxage)

        if code in (status.OK, status.WARN) and self._timeoutTime:
            if not self.isAtTarget(self.read(maxage)):
                code = status.BUSY
                msg = statusString('target not yet reached', msg)
            else:
                self._targetReached()
        if code == status.BUSY:
            if self.isTimedOut():
                code = self.timeout_status
                msg = statusString('movement timed out', msg)
                # only call once per timeout, flag is reset in Device.start()
                if not self._timeoutActionCalled and \
                        session.mode in (MASTER, MAINTENANCE):
                    try:
                        if hasattr(self, 'timeoutAction'):
                            self.timeoutAction()
                            self._timeoutActionCalled = True
                            return self._combinedStatus(maxage)
                    except Exception:
                        self.log.exception('error calling timeout action',
                                           exc=True)
                    finally:
                        self._timeoutActionCalled = True
            elif self._timesout:
                # give indication about the phase of the movement
                for m, t in self._timesout or []:
                    if t > currenttime():
                        msg = statusString(m, msg)
                        break
        elif code == status.ERROR:
            self._timeoutActionCalled = True

        return (code, msg)
Exemplo n.º 13
0
class DoubleMotorNOK(SequencerMixin, CanReference, PseudoNOK, HasPrecision,
                     Moveable):
    """NOK using two axes.

    If backlash is negative, approach form the negative side (default),
    else approach from the positive side.
    If backlash is zero, don't mind and just go to the target.
    """

    attached_devices = {
        'motor_r': Attach('NOK moving motor, reactor side', Moveable),
        'motor_s': Attach('NOK moving motor, sample side', Moveable),
    }

    parameters = {
        'mode':
        Param('Beam mode',
              type=oneof(*MODES),
              settable=True,
              userparam=True,
              default='ng',
              category='experiment'),
        'nok_motor':
        Param('Position of the motor for this NOK',
              type=tupleof(float, float),
              settable=False,
              unit='mm',
              category='general'),
        'inclinationlimits':
        Param('Allowed range for the positional '
              'difference',
              type=limits,
              mandatory=True),
        'backlash':
        Param('Backlash correction in phys. units',
              type=float,
              default=0.,
              unit='main'),
        'offsets':
        Param('Offsets of NOK-Motors (reactor side, sample side)',
              type=tupleof(float, float),
              default=(0., 0.),
              settable=False,
              unit='main',
              category='offsets'),
    }

    parameter_overrides = {
        'precision':
        Override(type=floatrange(0, 100)),
        'masks':
        Override(type=dictwith(**{name: float
                                  for name in MODES}),
                 unit='',
                 mandatory=True),
    }

    valuetype = tupleof(float, float)
    _honor_stop = True

    @lazy_property
    def _devices(self):
        return self._attached_motor_r, self._attached_motor_s

    def doInit(self, mode):
        for dev in self._devices:
            if hasattr(dev, 'backlash') and dev.backlash != 0:
                self.log.warning(
                    'Attached Device %s should not have a '
                    'non-zero backlash!', dev)

    def doRead(self, maxage=0):
        return [
            dev.read(maxage) - ofs - self.masks[self.mode]
            for dev, ofs in zip(self._devices, self.offsets)
        ]

    def doIsAllowed(self, targets):
        target_r, target_s = targets
        target_r += self.offsets[0]
        target_s += self.offsets[1]

        incmin, incmax = self.inclinationlimits

        inclination = target_s - target_r
        if not incmin <= inclination <= incmax:
            return False, 'Inclination %.2f out of limit (%.2f, %.2f)!' % (
                inclination, incmin, incmax)

        for dev in self._devices:
            res = dev.isAllowed(target_r)
            if not res[0]:
                return res

        # no problems detected, so it should be safe to go there....
        return True, ''

    def doIsAtTarget(self, targets):
        traveldists = [
            target - (akt + ofs)
            for target, akt, ofs in zip(targets, self.read(0), self.offsets)
        ]
        self.log.debug('doIsAtTarget', targets, 'traveldists', traveldists)
        return max(abs(v) for v in traveldists) <= self.precision

    def doStop(self):
        SequencerMixin.doStop(self)
        for dev in self._devices:
            dev.stop()
        try:
            self.wait()
        finally:
            self.reset()

    def doStart(self, targets):
        """Generate and start a sequence if none is running.

        The sequence is optimised for negative backlash.
        It will first move both motors to the lowest value of
        (target + backlash, current_position) and then
        to the final target.
        So, inbetween, the NOK should be parallel to the beam.
        """
        if self._seq_is_running():
            raise MoveError(self, 'Cannot start device, it is still moving!')

        # check precision, only move if needed!
        if self.isAtTarget(targets):
            return

        devices = self._devices

        # XXX: backlash correction and repositioning later

        # build a moving sequence
        sequence = []

        # now go to target
        sequence.append([
            SeqDev(d, t + ofs + self.masks[self.mode], stoppable=True)
            for d, t, ofs in zip(devices, targets, self.offsets)
        ])

        # now go to target again
        sequence.append([
            SeqDev(d, t + ofs + self.masks[self.mode], stoppable=True)
            for d, t, ofs in zip(devices, targets, self.offsets)
        ])

        self.log.debug('Seq: %r', sequence)
        self._startSequence(sequence)

    def doReset(self):
        multiReset(self._motors)
Exemplo n.º 14
0
class DoubleMotorBeckhoff(PseudoNOK, BeckhoffMotorBase):
    """a double motor Beckhoff knows both axis at once!
    It comunicats direktly by modbuss
    """

    hardware_access = True

    parameters = {
        'addresses':
        Param('addresses of each motor',
              type=tupleof(motoraddress, motoraddress),
              mandatory=True,
              settable=False,
              userparam=False),
        'mode':
        Param('Beam mode',
              type=oneof(*MODES),
              settable=True,
              userparam=True,
              default='ng',
              category='experiment'),
        'motortemp':
        Param('Motor temperature',
              type=tupleof(float, float),
              settable=False,
              userparam=True,
              volatile=True,
              unit='degC',
              fmtstr='%.1f, %.1f'),
        '_rawvalues':
        Param('Raw positions',
              type=tupleof(float, float),
              internal=True,
              category='experiment'),
    }

    parameter_overrides = {
        'ruler':
        Override(type=tupleof(float, float),
                 mandatory=True,
                 settable=False,
                 userparam=False),
    }

    valuetype = tupleof(float, float)

    def doReference(self):
        """Reference the NOK.

        Just set the do_reference bit and check for completion
        """
        self.log.error('nope')

    def doReadMaxvalue(self):
        return 1111

    def doReadMinvalue(self):
        return -1111

    def doReadMotortemp(self):
        # in degC
        self.log.debug('DoubleMotorBeckhoff doReadMotortemp')
        return self._HW_readParameter('motorTemp')

    def doReadFirmware(self):
        # TODO self._HW_readParameter('firmwareVersion'))
        return 'V%.1f' % (0.1 * 0)

    def doReadVmax(self):
        # TODO self._speed2phys(self._HW_readParameter('vMax'))
        return 0

    def doReadRefpos(self):
        # TODO self._steps2phys(self._HW_readParameter('refPos'))
        return 0

    def doReadPotentiometer(self):
        return [self._fit(a)
                for a in self._HW_readParameter('potentiometer')][0]

    def _writeUpperControlWord(self, value):
        self.log.debug('_writeUpperControlWord 0x%04x', value)
        value = int(value) & 0xffff
        self._dev.WriteOutputWord((self.addresses[0] + 1, value))
        self._dev.WriteOutputWord((self.addresses[1] + 1, value))

    def _readReturn(self):
        value = []
        for i in range(2):
            valuel = self._dev.ReadOutputWords((self.addresses[i] + 8, 2))
            self.log.debug('_readReturn %d: -> %d (0x%08x)', i, valuel, valuel)
            value.append(struct.unpack('=i', struct.pack('<2H', *valuel))[0])
        return value

    def _HW_readParameter(self, index):
        if index not in self.HW_readable_Params:
            raise UsageError('Reading not possible for parameter index %s' %
                             index)

        index = self.HW_readable_Params.get(index, index)
        self.log.debug('readParameter %d', index)

        for i in range(10):
            self._writeUpperControlWord((index << 8) | 4)
            session.delay(0.15)
            stat = self._areadStatusWord(self.addresses[0])
            self.log.debug('readStatusWord 0 %d', stat)
            stat = self._areadStatusWord(self.addresses[0])
            self.log.debug('readStatusWord 1 %d', stat)
            if (stat & 0x8000) != 0:
                if i > 0:
                    self.log.debug('readParameter %d', i + 1)
                return self._readReturn()
            if (stat & 0x4000) != 0:
                raise UsageError(
                    self, 'NACK ReadPar command not recognized '
                    'by HW, please retry later...')
        raise UsageError(
            self, 'ReadPar command not recognized by HW, please '
            'retry later ...')

    def _areadPosition(self, address):
        value = self._dev.ReadOutputWords((address + 6, 2))
        value = struct.unpack('=i', struct.pack('<2H', *value))[0]
        self.log.debug('_readPosition: -> %d steps', value)
        return value

    def _awriteDestination(self, value, address):
        self.log.debug('_writeDestination %r', value)
        value = struct.unpack('<2H', struct.pack('=i', value))
        self._dev.WriteOutputWords(tuple([address + 2]) + value)

    def _awriteControlBit(self, bit, value, numbits, address):
        self.log.debug('_writeControlBit %r, %r', bit, value)
        self._dev.WriteOutputWord(
            (address, (value & ((1 << int(numbits)) - 1)) << int(bit)))
        session.delay(0.1)  # work around race conditions....

    def _asteps2phys(self, steps, ruler):
        value = steps / float(self.slope) - ruler
        self.log.debug('_steps2phys ruler: %r steps -> %s', steps,
                       self.format(value, unit=True))
        return value

    def _aphys2steps(self, value, ruler):
        steps = int((value + ruler) * float(self.slope))
        self.log.debug('_phys2steps ruler: %s -> %r steps',
                       self.format(value, unit=True), steps)
        return steps

    def _indexReadPosition(self, i):
        return self._asteps2phys(self._areadPosition(self.addresses[i]),
                                 self.ruler[i])

    def _areadStatusWord(self, address):
        value = self._dev.ReadOutputWords((address + 4, 1))[0]
        self.log.debug('_areadStatusWord 0x%04x', value)
        return value

    def _areadErrorWord(self, address):
        value = self._dev.ReadOutputWords((address + 5, 1))[0]
        self.log.debug('_readErrorWord 0x%04x', value)
        return value

    def _indexHW_status(self, i):
        """Used status bits."""
        # read HW values
        errval = self._areadErrorWord(self.addresses[i])
        statval = (self._areadStatusWord(self.addresses[i])
                   ^ self.HW_Status_Inv) & ~self.HW_Status_Ign

        msg = bitDescription(statval, *self.HW_Statusbits)
        self.log.debug('HW_status: (%x) %s', statval, msg)
        if errval:
            errnum = errval
            return status.ERROR, 'ERROR %d: %s, %s' % (
                errnum,
                self.HW_Errors.get(errnum,
                                   'Unknown Error {0:d}'.format(errnum)), msg)

        for mask, stat in self.HW_Status_map:
            if statval & mask:
                return stat, msg

        return status.OK, msg if msg else 'Ready'

    def _HW_wait_while_HOT(self):
        sd = 6.5
        anz = int(round(self.waittime * 60 / sd))
        # Pech bei 2.session
        for a in range(anz):
            temp = max(self.motortemp)
            if temp < self.maxtemp:  # wait if temp>33 until temp<26
                if a:
                    self.log.info('%d Celsius continue', temp)
                else:
                    self.log.debug('%d Celsius continue', temp)
                return True
            self.log.info('%d Celsius Timeout in: %.1f min', temp,
                          (anz - a) * sd / 60)
            session.delay(sd)
        raise NicosTimeoutError('HW still HOT after {0:d} min'.format(
            self.waittime))

    def doRead(self, maxage=0):
        self.log.debug('DoubleMotorBeckhoff read')
        res = [self._indexReadPosition(0), self._indexReadPosition(1)]
        self.log.debug('%s', res)
        self._setROParam('_rawvalues', res)
        return res

    def doStatus(self, maxage=0):
        self.log.debug('DoubleMotorBeckhoff status')
        lowlevel = BaseSequencer.doStatus(self, maxage)
        if lowlevel[0] == status.BUSY:
            return lowlevel
        akt = [self._indexHW_status(0), self._indexHW_status(1)]
        self.log.debug('Status: %s', akt)
        msg = [st[1] for st in akt]
        self.log.debug('Status: %s', msg)
        return (max([st[0] for st in akt]),
                ', '.join(msg) if msg.count(msg[0]) == 1 else msg[0])

    def _generateSequence(self, target, indexes, code):
        self.log.debug('DoubleMotorBeckhoff Seq generated %s %s 0x%X', target,
                       indexes, code)
        seq = [SeqMethod(self, '_HW_wait_while_HOT')]
        for i in indexes:
            seq.append(
                SeqMethod(self, '_awriteDestination',
                          self._aphys2steps(target[i], self.ruler[i]),
                          self.addresses[i]))
            seq.append(
                SeqMethod(self, '_awriteControlBit', 0, code, 10,
                          self.addresses[i]))
        return seq

    def doStart(self, target):
        self.log.debug('DoubleMotorBeckhoff move to %s', target)
        if self._seq_is_running():
            raise MoveError(self, 'Cannot start device, it is still moving!')
        self._startSequence(self._generateSequence(target, [0, 1], 0x304))
Exemplo n.º 15
0
 def _tuple(members, **kwds):
     return tupleof(*(get_validator(**m) for m in members))
Exemplo n.º 16
0
class ImagePlateImage(ImageChannelMixin, PassiveChannel):
    """Represents the client to a MAATEL Image Plate Detector."""

    MAP_SHAPE = {
        125: (900, 10000),
        250: (900, 5000),
        500: (900, 2500),
    }

    attached_devices = {
        "imgdrum":      Attach("Image Plate Detector Drum "
                               "control device.", ImagePlateDrum),
    }

    parameters = {
        "erase": Param("Erase image plate on next start?", type=bool,
                       settable=True, mandatory=False, default=True),
        "roi": Param("Region of interest",
                     type=tupleof(int, int, int, int),
                     default=(0, 0, 0, 0),
                     settable=True, volatile=True,
                     category="general"),
        "pixelsize": Param("Pixel size in microns",
                           type=oneof(125, 250, 500), default=500,
                           settable=True, volatile=True, category="general"),
        "file": Param("Image file location on maatel computer",
                      type=str, settable=True, volatile=True,
                      category="general"),
        "readout_millis": Param("Timeout in ms for the readout",
                                type=int, settable=True, default=60000),
    }

    def doInit(self, mode):
        self.arraydesc = ArrayDesc('data',
                                   self.MAP_SHAPE[self.pixelsize],
                                   numpy.uint16)

    def doPrepare(self):
        # erase and expo position
        if self.erase:
            self._attached_imgdrum.maw(ImagePlateDrum.POS_ERASE)
        self._attached_imgdrum.maw(ImagePlateDrum.POS_EXPO)

    def valueInfo(self):
        # no readresult -> no values
        return ()

    def doReadArray(self, quality):
        if quality == FINAL:
            # start readout
            self._attached_imgdrum.maw(ImagePlateDrum.POS_READ)
            narray = None
            timeout = self._attached_imgdrum._dev.get_timeout_millis()
            self._attached_imgdrum._dev.set_timeout_millis(self.readout_millis)
            try:
                narray = self._attached_imgdrum._dev.Bitmap16Bit
            finally:
                self._attached_imgdrum._dev.set_timeout_millis(timeout)
            return narray
        return None

    def doReadRoi(self):
        return (0, self._attached_imgdrum._dev.InterestZoneY, 1250,
                self._attached_imgdrum._dev.InterestZoneH)

    def doReadPixelsize(self):
        return self._attached_imgdrum._dev.PixelSize

    def doReadFile(self):
        return self._attached_imgdrum._dev.ImageFile

    def doWriteRoi(self, value):
        self.log.warning("setting x offset and width are not supported "
                         "- ignored.")
        self._attached_imgdrum._dev.InterestZoneY = value[1]
        self._attached_imgdrum._dev.InterestZoneH = value[3]

    def doWritePixelsize(self, value):
        self._attached_imgdrum._dev.PixelSize = value
        self.arraydesc = ArrayDesc('data', self.MAP_SHAPE[value], numpy.uint16)

    def doWriteFile(self, value):
        self._attached_imgdrum._dev.ImageFile = value
Exemplo n.º 17
0
class TofChannel(PyTangoDevice, ImageChannelMixin, PassiveChannel):
    """Basic Tango Device for TofDetector."""

    STRSHAPE = ['x', 'y', 'z', 't']

    parameters = {
        'detshape':     Param('Shape of tof detector', type=dictof(str, int)),
        'timechannels': Param('Number of time channels - if set to 1 TOF mode '
                              'is disabled', type=intrange(1, 1024),
                              settable=True),
        'divisor':      Param('Width of a time channel',
                              type=int, unit='0.1us', settable=True),
        'delay':        Param('Offset delay in measure begin', type=int,
                              unit='0.1us', settable=True),
        'readchannels': Param('Tuple of (start, end) channel numbers will be '
                              'returned by a read', type=tupleof(int, int),
                              default=(0, 0), settable=True, mandatory=True),
        'readtimechan': Param('Tuple of (start, end) integrated time channels '
                              'will be returned by a read',
                              type=tupleof(int, int),
                              default=(0, 0), settable=True, mandatory=True),
    }

    def doInit(self, mode):
        self.arraydesc = ArrayDesc('data',
                                   (self.detshape.get('x', 1),
                                    self.detshape.get('t', 1)),
                                   numpy.uint32)
        if mode != SIMULATION:
            self._dev.set_timeout_millis(10000)

    def doPrepare(self):
        self._dev.Clear()
        PyTangoDevice._hw_wait(self)
        self.log.debug("Detector cleared")
        self._dev.Prepare()

    def doStart(self):
        start, end = self.readchannels
        self.readresult = [0] * (end - start + 1)
        self._dev.Start()
        self.log.debug("Detector started")

    def doFinish(self):
        self._dev.Stop()
        session.delay(0.2)
        PyTangoDevice._hw_wait(self)

    def doStop(self):
        self._dev.Stop()

    def doPause(self):
        self.doFinish()
        return True

    def doResume(self):
        self.doStart()

    def doReadTimechannels(self):
        return self._dev.timeChannels

    def doWriteTimechannels(self, value):
        self._dev.timeChannels = value
        self._pollParam('detshape')

    def doReadDivisor(self):
        return self._dev.timeInterval

    def doWriteDivisor(self, value):
        self._dev.timeInterval = value

    def doReadDelay(self):
        return self._dev.delay

    def doWriteDelay(self, value):
        self._dev.delay = value

    def doReadDetshape(self):
        shvalue = self._dev.detectorSize
        return {'x': shvalue[0], 't': shvalue[3]}

    def valueInfo(self):
        start, end = self.readchannels
        return tuple(Value("chan-%d" % i, unit="cts", errors="sqrt",
                           type="counter", fmtstr="%d")
                     for i in range(start, end + 1))

    def doReadArray(self, quality):
        self.log.debug("Tof Detector read image")
        start, end = self.readchannels
        # get current data array from detector
        array = numpy.asarray(self._dev.value, numpy.uint32)
        array = array.reshape(self.detshape['t'], self.detshape['x'])
        if self.timechannels > 1:
            startT, endT = self.readtimechan
            res = array[startT:endT+1].sum(axis=0)[start:end+1]
        else:
            res = array[0, start:end+1]
        self.readresult = res.tolist()
        return array