#!/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
))