Source code for instruments.lakeshore.lakeshore475

#!/usr/bin/env python
"""
Provides support for the Lakeshore 475 Gaussmeter.
"""

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

from enum import IntEnum

from instruments.generic_scpi import SCPIInstrument
from instruments.units import ureg as u
from instruments.util_fns import assume_units, bool_property

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

LAKESHORE_FIELD_UNITS = {1: u.gauss, 2: u.tesla, 3: u.oersted, 4: u.amp / u.meter}

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

LAKESHORE_FIELD_UNITS_INV = {v: k for k, v in LAKESHORE_FIELD_UNITS.items()}
LAKESHORE_TEMP_UNITS_INV = {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 instruments.units as u >>> gm = ik.lakeshore.Lakeshore475.open_gpibusb('/dev/ttyUSB0', 1) >>> print(gm.field) >>> gm.field_units = u.tesla >>> gm.field_setpoint = 0.05 * u.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: `~pint.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: `~pint.Unit` """ value = int(self.query("UNIT?")) return LAKESHORE_FIELD_UNITS[value] @field_units.setter def field_units(self, newval): if isinstance(newval, u.Unit): if newval in LAKESHORE_FIELD_UNITS_INV: self.sendcmd(f"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: `~pint.Unit` """ value = int(self.query("TUNIT?")) return LAKESHORE_TEMP_UNITS[value] @temp_units.setter def temp_units(self, newval): if isinstance(newval, u.Unit): if newval in LAKESHORE_TEMP_UNITS_INV: self.sendcmd(f"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 `~pint.Quantity`) or assumed to be of units Gauss. :type: `~pint.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): expected_units = self.field_units newval = assume_units(newval, u.gauss) if newval.units != expected_units: raise ValueError( f"Field setpoint must be specified in the same units " f"that the field units are currently set to. Attempts units of " f"{newval.units}, currently expecting {expected_units}." ) self.sendcmd(f"CSETP {newval.magnitude}") @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 `~pint.Quantity` """ params = self.query("CPARAM?").strip().split(",") params = [float(x) for x in params] params[2] = params[2] * self.field_units / u.minute params[3] = params[3] * u.volt / u.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") p, i, ramp_rate, control_slope_lim = newval expected_units = self.field_units / u.minute ramp_rate = assume_units(ramp_rate, expected_units) if ramp_rate.units != expected_units: raise ValueError( f"Field control params ramp rate must be specified in the same units " f"that the field units are currently set to, per minute. Attempts units of " f"{ramp_rate.units}, currently expecting {expected_units}." ) ramp_rate = float(ramp_rate.magnitude) unit = u.volt / u.minute control_slope_lim = float( assume_units(control_slope_lim, unit).to(unit).magnitude ) self.sendcmd(f"CPARAM {p},{i},{ramp_rate},{control_slope_lim}") @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 `~pint.Quantity`) or assumed to be of current field units / minute. :type: `~pint.Quantity` """ return self.field_control_params[2] @ramp_rate.setter def ramp_rate(self, newval): unit = self.field_units / u.minute newval = float(assume_units(newval, unit).to(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 `~pint.Quantity`) or assumed to be of units volt / minute. :type: `~pint.Quantity` """ return self.field_control_params[3] @control_slope_limit.setter def control_slope_limit(self, newval): unit = u.volt / u.minute newval = float(assume_units(newval, unit).to(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( "Peak measurement type setting must be a " "`Lakeshore475.PeakMode` value, got {} " "instead.".format(type(peak_mode)) ) if not isinstance(peak_disp, Lakeshore475.PeakDisplay): raise TypeError( "Peak display 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 ) )