Source code for instruments.lakeshore.lakeshore475

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Provides support for the Lakeshore 475 Gaussmeter.
"""

# IMPORTS #####################################################################

from __future__ import absolute_import
from __future__ import division

from builtins import range
from enum import IntEnum

import quantities as pq

from instruments.generic_scpi import SCPIInstrument
from instruments.util_fns import assume_units, bool_property

# CONSTANTS ###################################################################

LAKESHORE_FIELD_UNITS = {
    1: pq.gauss,
    2: pq.tesla,
    3: pq.oersted,
    4: pq.CompoundUnit('A/m')
}

LAKESHORE_TEMP_UNITS = {
    1: pq.celsius,
    2: pq.kelvin
}

LAKESHORE_FIELD_UNITS_INV = dict((v, k) for k, v in
                                 LAKESHORE_FIELD_UNITS.items())
LAKESHORE_TEMP_UNITS_INV = dict((v, k) for k, v in
                                LAKESHORE_TEMP_UNITS.items())

# CLASSES #####################################################################


[docs]class Lakeshore475(SCPIInstrument): """ The Lakeshore475 is a DSP Gaussmeter with field ranges from 35mG to 350kG. Example usage: >>> import instruments as ik >>> import quantities as pq >>> gm = ik.lakeshore.Lakeshore475.open_gpibusb('/dev/ttyUSB0', 1) >>> print(gm.field) >>> gm.field_units = pq.tesla >>> gm.field_setpoint = 0.05 * pq.tesla """ # ENUMS ##
[docs] class Mode(IntEnum): """ Enum containing valid measurement modes for the Lakeshore 475 """ dc = 1 rms = 2 peak = 3
[docs] class Filter(IntEnum): """ Enum containing valid filter modes for the Lakeshore 475 """ wide = 1 narrow = 2 lowpass = 3
[docs] class PeakMode(IntEnum): """ Enum containing valid peak modes for the Lakeshore 475 """ periodic = 1 pulse = 2
[docs] class PeakDisplay(IntEnum): """ Enum containing valid peak displays for the Lakeshore 475 """ positive = 1 negative = 2 both = 3
# PROPERTIES ## @property def field(self): """ Read field from connected probe. :type: `~quantities.quantity.Quantity` """ return float(self.query('RDGFIELD?')) * self.field_units @property def field_units(self): """ Gets/sets the units of the Gaussmeter. Acceptable units are Gauss, Tesla, Oersted, and Amp/meter. :type: `~quantities.unitquantity.UnitQuantity` """ value = int(self.query('UNIT?')) return LAKESHORE_FIELD_UNITS[value] @field_units.setter def field_units(self, newval): if isinstance(newval, pq.unitquantity.UnitQuantity): if newval in LAKESHORE_FIELD_UNITS_INV: self.sendcmd('UNIT ' + LAKESHORE_FIELD_UNITS_INV[newval]) else: raise ValueError('Not an acceptable Python quantities object') else: raise TypeError('Field units must be a Python quantity') @property def temp_units(self): """ Gets/sets the temperature units of the Gaussmeter. Acceptable units are celcius and kelvin. :type: `~quantities.unitquantity.UnitQuantity` """ value = int(self.query('TUNIT?')) return LAKESHORE_TEMP_UNITS[value] @temp_units.setter def temp_units(self, newval): if isinstance(newval, pq.unitquantity.UnitQuantity): if newval in LAKESHORE_TEMP_UNITS_INV: self.sendcmd('TUNIT ' + LAKESHORE_TEMP_UNITS_INV[newval]) else: raise TypeError('Not an acceptable Python quantities object') else: raise TypeError('Temperature units must be a Python quantity') @property def field_setpoint(self): """ Gets/sets the final setpoint of the field control ramp. :units: As specified (if a `~quantities.Quantity`) or assumed to be of units Gauss. :type: `~quantities.quantity.Quantity` with units Gauss """ value = self.query('CSETP?').strip() units = self.field_units return float(value) * units @field_setpoint.setter def field_setpoint(self, newval): units = self.field_units newval = float(assume_units(newval, pq.gauss).rescale(units).magnitude) self.sendcmd('CSETP {}'.format(newval)) @property def field_control_params(self): """ Gets/sets the parameters associated with the field control ramp. These are (in this order) the P, I, ramp rate, and control slope limit. :type: `tuple` of 2 `float` and 2 `~quantities.quantity.Quantity` """ params = self.query('CPARAM?').strip().split(',') params = [float(x) for x in params] params[2] = params[2] * self.field_units / pq.minute params[3] = params[3] * pq.volt / pq.minute return tuple(params) @field_control_params.setter def field_control_params(self, newval): if not isinstance(newval, tuple): raise TypeError('Field control parameters must be specified as ' ' a tuple') newval = list(newval) newval[0] = float(newval[0]) newval[1] = float(newval[1]) unit = self.field_units / pq.minute newval[2] = float( assume_units(newval[2], unit).rescale(unit).magnitude) unit = pq.volt / pq.minute newval[3] = float( assume_units(newval[3], unit).rescale(unit).magnitude) self.sendcmd('CPARAM {},{},{},{}'.format( newval[0], newval[1], newval[2], newval[3], )) @property def p_value(self): """ Gets/sets the P value for the field control ramp. :type: `float` """ return self.field_control_params[0] @p_value.setter def p_value(self, newval): newval = float(newval) values = list(self.field_control_params) values[0] = newval self.field_control_params = tuple(values) @property def i_value(self): """ Gets/sets the I value for the field control ramp. :type: `float` """ return self.field_control_params[1] @i_value.setter def i_value(self, newval): newval = float(newval) values = list(self.field_control_params) values[1] = newval self.field_control_params = tuple(values) @property def ramp_rate(self): """ Gets/sets the ramp rate value for the field control ramp. :units: As specified (if a `~quantities.Quantity`) or assumed to be of current field units / minute. :type: `~quantities.quantity.Quantity` """ return self.field_control_params[2] @ramp_rate.setter def ramp_rate(self, newval): unit = self.field_units / pq.minute newval = float(assume_units(newval, unit).rescale(unit).magnitude) values = list(self.field_control_params) values[2] = newval self.field_control_params = tuple(values) @property def control_slope_limit(self): """ Gets/sets the I value for the field control ramp. :units: As specified (if a `~quantities.Quantity`) or assumed to be of units volt / minute. :type: `~quantities.quantity.Quantity` """ return self.field_control_params[3] @control_slope_limit.setter def control_slope_limit(self, newval): unit = pq.volt / pq.minute newval = float(assume_units(newval, unit).rescale(unit).magnitude) values = list(self.field_control_params) values[3] = newval self.field_control_params = tuple(values) control_mode = bool_property( command="CMODE", inst_true="1", inst_false="0", doc=""" Gets/sets the control mode setting. False corresponds to the field control ramp being disables, while True enables the closed loop PI field control. :type: `bool` """ ) # METHODS ## # pylint: disable=too-many-arguments
[docs] def change_measurement_mode(self, mode, resolution, filter_type, peak_mode, peak_disp): """ Change the measurement mode of the Gaussmeter. :param mode: The desired measurement mode. :type mode: `Lakeshore475.Mode` :param `int` resolution: Digit resolution of the measured field. One of `{3|4|5}`. :param filter_type: Specify the signal filter used by the instrument. Available types include wide band, narrow band, and low pass. :type filter_type: `Lakeshore475.Filter` :param peak_mode: Peak measurement mode to be used. :type peak_mode: `Lakeshore475.PeakMode` :param peak_disp: Peak display mode to be used. :type peak_disp: `Lakeshore475.PeakDisplay` """ if not isinstance(mode, Lakeshore475.Mode): raise TypeError("Mode setting must be a " "`Lakeshore475.Mode` value, got {} " "instead.".format(type(mode))) if not isinstance(resolution, int): raise TypeError('Parameter "resolution" must be an integer.') if not isinstance(filter_type, Lakeshore475.Filter): raise TypeError("Filter type setting must be a " "`Lakeshore475.Filter` value, got {} " "instead.".format(type(filter_type))) if not isinstance(peak_mode, Lakeshore475.PeakMode): raise TypeError("Filter type setting must be a " "`Lakeshore475.PeakMode` value, got {} " "instead.".format(type(peak_mode))) if not isinstance(peak_disp, Lakeshore475.PeakDisplay): raise TypeError("Filter type setting must be a " "`Lakeshore475.PeakDisplay` value, got {} " "instead.".format(type(peak_disp))) mode = mode.value filter_type = filter_type.value peak_mode = peak_mode.value peak_disp = peak_disp.value # Parse the resolution if resolution in range(3, 6): resolution -= 2 else: raise ValueError('Only 3,4,5 are valid resolutions.') self.sendcmd('RDGMODE {},{},{},{},{}'.format( mode, resolution, filter_type, peak_mode, peak_disp ))