From 3387f5a6822ed63a0d7c8032119194e2679dde96 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 12:26:30 -0400 Subject: [PATCH 1/7] Started updating SRS830 class --- python/src/instruments/srs/srs830.py | 78 ++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index 511ca1a..62a887a 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -1,22 +1,80 @@ #!/usr/bin/python -# Filename: srs830.py +# -*- coding: utf-8 -*- +## +# srs830.py: Driver for the SRS830 lock-in amplifier. +## +# © 2013 Steven Casagrande (scasagrande@galvant.ca). +# +# This file is a part of the GPIBUSB adapter project. +# Licensed under the AGPL version 3. +## +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +## -# Original author: Steven Casagrande (stevencasagrande@gmail.com) -# 2012 +## FEATURES #################################################################### -# This work is released under the Creative Commons Attribution-Sharealike 3.0 license. -# See http://creativecommons.org/licenses/by-sa/3.0/ or the included license/LICENSE.TXT file for more information. +from __future__ import division -# Attribution requirements can be found in license/ATTRIBUTION.TXT +## IMPORTS ##################################################################### -from instruments.abstract_instruments import Instrument import math import time -class SRS830(Instrument): - def __init__(self, port, address,timeout_length): +from flufl.enum import Enum +from flufl.enum._enum import EnumValue + +from instruments.generic_scpi import SCPIInstrument +import instruments.abstract_instruments.gi_gpib as gw +import instruments.abstract_instruments.serialwrapper as sw + +## ENUMS ####################################################################### + +class SRS830FreqSource(Enum): + external = 'ext' + internal = 'int' + +## CLASSES ##################################################################### + +class SRS830(SCPIInstrument): + def __init__(self, port, address, timeout_length, outx_mode=None): super(SRS830, self).__init__(self,port,address,timeout_length) - self.write('OUTX 1') # Set the device responce port to GPIB + if outx_mode is 1: + self.sendcmd('OUTX 1') + elif outx_mode is 2: + self.sendcmd('OUTX 2') + else: + if isinstance(self._file, gw.GPIBWarapper): + self.sendcmd('OUTX 1') + elif isinstance(self._file, sw.SerialWrapper): + self.sendcmd('OUTX 2') + else: + print 'OUTX command has not been set. Instrument behavour is '\ + 'unknown.' + + ## PROPERTIES ## + + @property + def freq_source(self): + return SRS830FreqSource[self.query('FMOD?')] + @freq_source.setter + def freq_source(self, newval): + if not isinstance(newval, EnumValue) or + (newval.enum is not SRS830FreqSource): + raise TypeError("Frequency source setting must be a " + "SRS830FreqSource value, got {} " + "instead.".format(type(newval))) + self.sendcmd('FMOD {}'.format(newval.value)) # Set Frequency Source def setFreqSource(self,source): From 16968436055199f81df7f94615be3797e36bdc9c Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 13:04:05 -0400 Subject: [PATCH 2/7] Added freq, phase, and amplitude properties --- python/src/instruments/srs/srs830.py | 90 ++++++++++------------------ 1 file changed, 30 insertions(+), 60 deletions(-) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index 62a887a..9d6c687 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -33,16 +33,18 @@ from flufl.enum import Enum from flufl.enum._enum import EnumValue +import quantities as pq from instruments.generic_scpi import SCPIInstrument import instruments.abstract_instruments.gi_gpib as gw import instruments.abstract_instruments.serialwrapper as sw +from instruments.util_fns import assume_units ## ENUMS ####################################################################### class SRS830FreqSource(Enum): - external = 'ext' - internal = 'int' + external = 'EXT' + internal = 'INT' ## CLASSES ##################################################################### @@ -75,80 +77,48 @@ def freq_source(self, newval): "SRS830FreqSource value, got {} " "instead.".format(type(newval))) self.sendcmd('FMOD {}'.format(newval.value)) - - # Set Frequency Source - def setFreqSource(self,source): - ''' - Function sets teh frequency source to either the internal - reference clock, or an external reference. - - source: {EXTernal|INTernal},string - ''' - if not isinstance(source,str): - raise Exception('Parameter must be a string.') - - source = source.lower() - - valid1 = ['ext','int'] - valid2 = ['external','internal'] - if source in valid1: - source = valid1.index(source) - elif source in valid2: - source = valid2.index(source) - else: - raise Exception('Only "external" and "internal" are valid freq sources.') - - self.write( 'FREQ ' + str(source) ) - - # Set Frequency - def setFreq(self,freq): - ''' - Function sers the internal reference frequency. This command only - works if the lock-in is set to use the internal reference. - freq: Desired frequency. Rounded to 5 digits or 0.0001Hz, whichever is larger. - freq: ,float - ''' - if not isinstance(freq,int) and not isinstance(freq,float): - raise Exception('Freq parameter must be an integer or a float.') + @property + def freq(self): + return pq.Quantity(float(self.query('FREQ?')),pq.hertz) + @freq.setter + def freq(self, newval): + newval = float(assume_units(newval, pq.Hz).rescale(pq.Hz).magnitude) - # Ensure that lock-in is not set to external freq ref - if( int(self.query('FMOD?')) == 0 ): - raise Exception('SRS830 set to external freq source, cannot change internal freq.') - - self.write( 'FREQ ' + str(freq) ) + self.sendcmd('FREQ {}'.format(newval)) - # Set Phase - def setPhase(self,phase): + @property + def phase(self): ''' Function sets the phase of the internal reference signal. phase: Desired phase phase = <-360...+729.99>,float ''' - if not isinstance(phase,int) and not isinstance(phase,float): - raise Exception('Phase parameter must be an integer or a float.') - - if ( (phase >= 730) or (phase < -360) ): - raise Exception('Phase must be -360 <= phase < +730 .') - - self.write( 'PHAS ' + str(phase) ) + return pq.Quantity(float(self.query('PHAS?')), pq.degrees) + phase.setter + def phase(self, newval): + newval = float(assume_units(newval, pq.degree) + .rescale(pq.degree).magnitude) + if (newval >= 730) or (newval <- 360): + raise ValueError('Phase must be -360 <= phase < +730') + self.sendcmd('PHAS {}'.format(newval)) - # Set Amplitude - def setAmplitude(self,amplitude): + @property + def amplitude(self): ''' Function sets the amplitude of the internal reference signal. amplitude: Desired peak-to-peak voltage amplitude = <0.004...5>,float ''' - if not isinstance(amplitude,int) and not isinstance(amplitude,float): - raise Exception('Amplitude parameter must be an integer or a float.') - - if ( (amplitude > 5) or (amplitude < 0.004) ): - raise Exception('Amplitude must be +0.004 <= amplitude <= +5 .') - - self.write( 'SLVL ' + str(amplitude) ) + return pq.Quantity(float(self.query('SLVL?')), pq.volt) + @amplitude.setter + def amplitude(self, newval): + newval = float(assume_units(newval, pq.volt).rescale(pq.volt).magnitude) + if ((newval > 5) or (newval < 0.004)): + raise ValueError('Amplitude must be +0.004 <= amplitude <= +5 .') + self.sendcmd('SLVL {}'.format(newval)) # Set Input Shield Grounding def setInputGround(self,grounding): From 801e7885246e1425c7afaced3d60873c3301c7f7 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 13:09:15 -0400 Subject: [PATCH 3/7] Added input shield grounding property --- python/src/instruments/srs/srs830.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index 9d6c687..b4b787d 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -45,6 +45,10 @@ class SRS830FreqSource(Enum): external = 'EXT' internal = 'INT' + +class SRS830InputShield(Enum): + floating = 0 + grounded = 1 ## CLASSES ##################################################################### @@ -119,6 +123,24 @@ def amplitude(self, newval): if ((newval > 5) or (newval < 0.004)): raise ValueError('Amplitude must be +0.004 <= amplitude <= +5 .') self.sendcmd('SLVL {}'.format(newval)) + + @property + def input_shield_grounding(self): + ''' + Function sets the input shield grounding to either 'float' or 'ground' + + grounding: Desired input shield grounding + grounding = {float|ground},string + ''' + return SRS830InputShield[self.query('IGND?')] + @input_shield_grounding.setter + def input_shield_grounding(self, newval): + if not isinstance(newval, EnumValue) or + (newval.enum is not SRS830InputShield): + raise TypeError("Frequency source setting must be a " + "SRS830InputShield value, got {} " + "instead.".format(type(newval))) + self.sendcmd('IGND {}'.format(newval.value)) # Set Input Shield Grounding def setInputGround(self,grounding): From 1c237bed29d99c6bd964f949463a64939b1069ff Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 13:15:15 -0400 Subject: [PATCH 4/7] Added coupling property --- python/src/instruments/srs/srs830.py | 56 +++++++++------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index b4b787d..0562331 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -43,12 +43,16 @@ ## ENUMS ####################################################################### class SRS830FreqSource(Enum): - external = 'EXT' - internal = 'INT' + external = 0 + internal = 1 class SRS830InputShield(Enum): floating = 0 grounded = 1 + +class SRS830Coupling(Enum): + ac = 0 + dc = 1 ## CLASSES ##################################################################### @@ -137,52 +141,28 @@ def input_shield_grounding(self): def input_shield_grounding(self, newval): if not isinstance(newval, EnumValue) or (newval.enum is not SRS830InputShield): - raise TypeError("Frequency source setting must be a " + raise TypeError("Input shield grounding setting must be a " "SRS830InputShield value, got {} " "instead.".format(type(newval))) self.sendcmd('IGND {}'.format(newval.value)) - # Set Input Shield Grounding - def setInputGround(self,grounding): - ''' - Function sets the input shield grounding to either 'float' or 'ground' - - grounding: Desired input shield grounding - grounding = {float|ground},string - ''' - if not isinstance(grounding,str): - raise Exception('Parameter "grounding" must be a string.') - - grounding = grounding.lower() - - valid = ['float','ground'] - if grounding in valid: - grounding = str( valid.index(grounding) ) - else: - raise Exception('Only "float" and "ground" are valid grounding input ground settings.') - - self.write( 'IGND ' + grounding ) - - # Set Input Coupling - def setInputCoupling(self,coupling): + @property + def coupling(self): ''' Function sets the input coupling to either 'ac' or 'dc' coupling: Desired input coupling mode coupling = {ac|dc},string ''' - if not isinstance(coupling,str): - raise Exception('Parameter "coupling" must be a string.') - - coupling = coupling.lower() - - valid = ['ac','dc'] - if coupling in valid: - coupling = str( valid.index(coupling) ) - else: - raise Exception('Only "ac" and "dc" are valid input coupling settings.') - - self.write( 'ICPL ' + coupling ) + return SRS830Coupling[self.query('ICPL?')] + @coupling.setter + def coupling(self, newval): + if not isinstance(newval, EnumValue) or + (newval.enum is not SRS830Coupling): + raise TypeError("Input coupling setting must be a " + "SRS830Coupling value, got {} " + "instead.".format(type(newval))) + self.sendcmd('ICPL {}'.format(newval.value)) # Set Data Sample Rate def setSampleRate(self,sampleRate): From d704db68226ddfb728ceeca2851280af07e2294f Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 13:34:01 -0400 Subject: [PATCH 5/7] Added sample rate property --- python/src/instruments/srs/srs830.py | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index 0562331..be16f43 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -54,6 +54,10 @@ class SRS830Coupling(Enum): ac = 0 dc = 1 +## CONSTANTS ################################################################### + +VALID_SAMPLE_RATES = [2.0**n for n in xrange(-4, 10)] + + ## CLASSES ##################################################################### class SRS830(SCPIInstrument): @@ -163,9 +167,9 @@ def coupling(self, newval): "SRS830Coupling value, got {} " "instead.".format(type(newval))) self.sendcmd('ICPL {}'.format(newval.value)) - - # Set Data Sample Rate - def setSampleRate(self,sampleRate): + + @property + def sample_rate(self): ''' Function sets the data sampling rate of the lock-in @@ -174,16 +178,19 @@ def setSampleRate(self,sampleRate): This means 2^n, n={-4...+9} sampleRate = {,TRIGGER} ''' - if isinstance(sampleRate,str): - sampleRate = sampleRate.lower() - - valid = [0.0625,0.125,0.25,0.5,1,2,4,8,16,32,64,128,256,512,'trigger'] - if sampleRate in valid: - sampleRate = str( valid.index(sampleRate) ) - else: - raise Exception('Data sample rate parameter can only be 2^n, n={-4..+9} or "trigger".') + return pq.Quantity(float(self.query('SRAT?')), pq.Hz) + @sample_rate.setter + def sample_rate(self, newval): + if isinstance(newval, str): + newval = newval.lower() + if newval == 'trigger': + self.sendcmd('SRAT 14') - self.write( 'SRAT ' + sampleRate ) + if newval in VALID_SAMPLE_RATES: + self.sendcmd('SRAT {}'.format(VALID_SAMPLE_RATES.index(newval))) + else: + raise ValueError('Valid samples rates given by {} and "trigger".' + .format(VALID_SAMPLE_RATES)) # Set End of Buffer Mode def setEndOfBufferMode(self,mode): From 8ebf8064df70ed0e776132cbfe5d1129c4a7e358 Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 15:46:41 -0400 Subject: [PATCH 6/7] Bug fixes, style changes, added some properties. --- python/src/instruments/srs/srs830.py | 250 +++++++++++++-------------- 1 file changed, 121 insertions(+), 129 deletions(-) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index be16f43..67b53fb 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -54,6 +54,14 @@ class SRS830Coupling(Enum): ac = 0 dc = 1 +class SRS830BufferMode(Enum): + one_shot = 0 + loop = 1 + +class SRS830TransferMode(Enum): + off = 0 + on = 2 + ## CONSTANTS ################################################################### VALID_SAMPLE_RATES = [2.0**n for n in xrange(-4, 10)] + @@ -192,88 +200,53 @@ def sample_rate(self, newval): raise ValueError('Valid samples rates given by {} and "trigger".' .format(VALID_SAMPLE_RATES)) - # Set End of Buffer Mode - def setEndOfBufferMode(self,mode): + @property + def buffer_mode(self): ''' Function sets the end of buffer mode mode: Desired end of buffer mode mode = {1SHOT,LOOP},string ''' - if not isinstance(mode,str): - raise Exception('Parameter "mode" must be a string.') - - mode = mode.lower() - valid = ['1shot','loop'] - - if mode in valid: - mode = str( valid.index(mode) ) - else: - raise Exception('Only "1shot" and "loop" are valid end of buffer modes.') - - self.write( 'SEND ' + mode ) + return SRS830BufferMode[self.query('SEND?')] + @buffer_mode.setter + def buffer_mode(self, newval): + if not isinstance(newval, EnumValue) or + (newval.enum is not SRS830BufferMode): + raise TypeError("Input coupling setting must be a " + "SRS830BufferMode value, got {} " + "instead.".format(type(newval))) + self.sendcmd('SEND {}'.format(newval.value)) - # Set Channel Display - def setChannelDisplay(self,channel,display,ratio): + @property + def num_data_points(self): ''' - % Function sets the display of the two channels. - % Channel 1 can display X, R, X Noise, Aux In 1, Aux In 2 - % Channel 2 can display Y, Theta, Y Noise, Aux In 3, Aux In 4 - % - % Channel 1 can have ratio of None, Aux In 1, Aux In 2 - % Channel 2 can have ratio of None, Aux In 3, Aux In 4 + Function checks number of data sets in SRS830 buffer. + Returns an integer. + ''' + return int( self.query('SPTS?') ) + + @property + def data_transfer(self): + ''' + % Function used to turn the data transfer from the lockin on or off % - % channel = {CH1|CH2|1|2},string/int - % display = {X|Y|R|THETA|XNOISE|YNOISE|AUX1|AUX2|AUX3|AUX4},string - % ratio = {NONE|AUX1|AUX2|AUX3|AUX4},string + % mode: + % mode = {ON|OFF},string ''' - if not isinstance(channel,str) and not isinstance(channel,int): - raise Exception('Parameter "channel" must be a string or integer.') - if not isinstance(display,str): - raise Exception('Parameter "display" must be a string.') - if not isinstance(ratio,str): - raise Exception('Parameter "ratio" must be a string.') - - if type(channel) == type(str()): - channel = channel.lower() - display = display.lower() - ratio = ratio.lower() - - if channel == 'ch1' or channel == '1' or channel == 1: - channel = '1' - valid = ['x','r','xnoise','aux1','aux2'] - if display in valid: - display = str( valid.index(display) ) - else: - raise Exception('Only "x" , "r" , "xnoise" , "aux1" and "aux2" are valid displays for channel 1.') - - valid = ['none','aux1','aux2'] - if ratio in valid: - ratio = str( valid.index(ratio) ) - else: - raise Exception('Only "none" , "aux1" and "aux2" are valid ratios for channel 1.') - - elif channel == 'ch2' or channel == '2' or channel == 2: - channel = '2' - valid = ['y','theta','ynoise','aux3','aux4'] - if display in valid: - display = str( valid.index(display) ) - else: - raise Exception('Only "y" , "theta" , "ynoise" , "aux3" and "aux4" are valid displays for channel 2.') - - valid = ['none','aux3','aux4'] - if ratio in valid: - ratio = str( valid.index(ratio) ) - else: - raise Exception('Only "none" , "aux3" and "aux4" are valid ratios for channel 2.') - - else: - raise Exception('Only "ch1" and "ch2" are valid channels.') - - self.write( 'DDEF %s,%s,%s' % (channel,display,ratio) ) + return SRS830TransferMode[self.query('FAST?')] + @data_transfer.setter + def data_transfer(self, newval): + if not isinstance(newval, EnumValue) or + (newval.enum is not SRS830TransferMode): + raise TypeError("Input coupling setting must be a " + "SRS830TransferMode value, got {} " + "instead.".format(type(newval))) + self.sendcmd('FAST {}'.format(newval.value)) - # Set the Channel Offset and Expand - def setOffsetExpand(self,mode,offset,expand): + ## METHODS ## + + def set_offset_expand(self,mode,offset,expand): ''' % Function sets the channel offset and expand parameters. % Offset is a percentage, and expand is given as a multiplication @@ -298,7 +271,8 @@ def setOffsetExpand(self,mode,offset,expand): if mode in valid: mode = valid.index(mode) + 1 else: - raise Exception('Only "x" , "y" and "r" are valid modes for setting the offset & expand.') + raise Exception('Only "x" , "y" and "r" are valid modes for ' + 'setting the offset & expand.') if type(offset) != type(int()) or type(offset) != type(float()): raise Exception('Offset parameter must be an integer or a float.') @@ -316,8 +290,7 @@ def setOffsetExpand(self,mode,offset,expand): self.write( 'OEXP %s,%s,%s' % (mode,offset,expand) ) - # Enable Auto Offset - def autoOffset(self,mode): + def auto_offset(self,mode): ''' % Function sets a specific channel mode to auto offset. This is the % same as pressing the auto offset key on the display. @@ -335,12 +308,12 @@ def autoOffset(self,mode): if mode in valid: mode = str( valid.index(mode) + 1 ) else: - raise Exception('Only "x" , "y" and "r" are valid modes for setting the auto offset.') + raise Exception('Only "x" , "y" and "r" are valid modes ' + 'for setting the auto offset.') self.write( 'AOFF ' + mode ) - # Enable Auto Phase - def autoPhase(self): + def auto_phase(self): ''' % Function sets the lock-in to auto phase. % This does the same thing as pushing the auto phase button. @@ -348,47 +321,22 @@ def autoPhase(self): % of time for the lock-in to finish. ''' self.write('APHS') - - # Set Data Transfer on/off - def dataTransfer(self,mode): - ''' - % Function used to turn the data transfer from the lockin on or off - % - % mode: - % mode = {ON|OFF},string - ''' - if not isinstance(mode,str): - raise Exception('Parameter "mode" must be a string.') - - mode = mode.lower() - - if mode == 'off': - mode = '0' - elif mode == 'on': - mode = '2' - else: - raise Exception('Only "on" and "off" are valid parameters for setDataTransfer.') - - self.write( 'FAST ' + mode ) - - # Start Scan - def startScan(self): + + def start_scan(self): ''' % After setting the data transfer on via the dataTransfer function, % this is used to start the scan. The scan starts after a delay of % 0.5 seconds. ''' self.write('STRD') - - # Pause Data Capture + def pause(self): ''' Has the instrument pause data capture. ''' self.write('PAUS') - # Start Data Transfer (wrapper) - def init(self,sampleRate,EoBMode): + def init(self, sample_rate, buffer_mode): ''' Wrapper function to prepare the srs830 for measurement. Sets both the data sampling rate and the end of buffer mode @@ -400,21 +348,20 @@ def init(self,sampleRate,EoBMode): mode = {1SHOT|LOOP},string ''' - self.clearDataBuffer() - self.setSampleRate(sampleRate) - self.setEndOfBufferMode(EoBMode) + self.clear_data_buffer() + self.sample_rate = sample_rate + self.buffer_mode = buffer_mode - # Take Data Snapshot (wrapper) - def startDataTransfer(self): + def start_data_transfer(self): ''' Wrapper function to start the actual data transfer. Sets the transfer mode to FAST2, and triggers the data transfer to start after a delay of 0.5 seconds. ''' - self.dataTransfer('on') - self.startScan() + self.data_transfer('on') + self.start_scan() - def dataSnap(self,mode1,mode2): + def data_snap(self,mode1,mode2): ''' Function takes a snapshot of the current parameters are defined by variables mode1 and mode2. @@ -452,8 +399,7 @@ def dataSnap(self,mode1,mode2): result = self.query( 'SNAP? %s,%s' % (mode1,mode2) ) return map( float, result.split(',') ) - # Read Data Buffer - def readDataBuffer(self,channel): + def read_data_buffer(self,channel): ''' Function reads the entire data buffer for a specific channel. Transfer is done in ASCII mode. Although binary would be faster, @@ -484,23 +430,13 @@ def readDataBuffer(self,channel): # calling method return map( float, self.query( 'TRCA?%s,0,%s' % (channel,N) ).split(',') ) - # Check number of data sets in buffer - def numDataPoints(self): - ''' - Function checks number of data sets in SRS830 buffer. - Returns an integer. - ''' - return int( self.query('SPTS?') ) - - # Clear data (channel) buffer - def clearDataBuffer(self): + def clear_data_buffer(self): ''' Clears the data buffer of the SRS830. ''' self.write('REST') - # Take measurement (wrapper function) - def takeMeasurement(self,sampleRate,numSamples): + def take_measurement(self, sampleRate, numSamples): numSamples = float( numSamples ) if numSamples > 16383: raise Exception('Number of samples cannot exceed 16383.') @@ -522,8 +458,64 @@ def takeMeasurement(self,sampleRate,numSamples): ch2 = self.readDataBuffer('ch2') return [ch1,ch2] + + def set_channel_display(self,channel,display,ratio): + ''' + % Function sets the display of the two channels. + % Channel 1 can display X, R, X Noise, Aux In 1, Aux In 2 + % Channel 2 can display Y, Theta, Y Noise, Aux In 3, Aux In 4 + % + % Channel 1 can have ratio of None, Aux In 1, Aux In 2 + % Channel 2 can have ratio of None, Aux In 3, Aux In 4 + % + % channel = {CH1|CH2|1|2},string/int + % display = {X|Y|R|THETA|XNOISE|YNOISE|AUX1|AUX2|AUX3|AUX4},string + % ratio = {NONE|AUX1|AUX2|AUX3|AUX4},string + ''' + if not isinstance(channel,str) and not isinstance(channel,int): + raise Exception('Parameter "channel" must be a string or integer.') + if not isinstance(display,str): + raise Exception('Parameter "display" must be a string.') + if not isinstance(ratio,str): + raise Exception('Parameter "ratio" must be a string.') + + if type(channel) == type(str()): + channel = channel.lower() + display = display.lower() + ratio = ratio.lower() + + if channel == 'ch1' or channel == '1' or channel == 1: + channel = '1' + valid = ['x','r','xnoise','aux1','aux2'] + if display in valid: + display = str( valid.index(display) ) + else: + raise Exception('Only "x" , "r" , "xnoise" , "aux1" and "aux2" are valid displays for channel 1.') - + valid = ['none','aux1','aux2'] + if ratio in valid: + ratio = str( valid.index(ratio) ) + else: + raise Exception('Only "none" , "aux1" and "aux2" are valid ratios for channel 1.') + + elif channel == 'ch2' or channel == '2' or channel == 2: + channel = '2' + valid = ['y','theta','ynoise','aux3','aux4'] + if display in valid: + display = str( valid.index(display) ) + else: + raise Exception('Only "y" , "theta" , "ynoise" , "aux3" and "aux4" are valid displays for channel 2.') + + valid = ['none','aux3','aux4'] + if ratio in valid: + ratio = str( valid.index(ratio) ) + else: + raise Exception('Only "none" , "aux3" and "aux4" are valid ratios for channel 2.') + + else: + raise Exception('Only "ch1" and "ch2" are valid channels.') + + self.write( 'DDEF %s,%s,%s' % (channel,display,ratio) ) From 99ae60eef12cdac71df154764afb705c1778311d Mon Sep 17 00:00:00 2001 From: Steven Casagrande Date: Tue, 16 Apr 2013 17:10:38 -0400 Subject: [PATCH 7/7] Removed an enum --- python/src/instruments/srs/srs830.py | 156 +++++++++++++-------------- 1 file changed, 75 insertions(+), 81 deletions(-) diff --git a/python/src/instruments/srs/srs830.py b/python/src/instruments/srs/srs830.py index 67b53fb..7cded8b 100644 --- a/python/src/instruments/srs/srs830.py +++ b/python/src/instruments/srs/srs830.py @@ -58,10 +58,6 @@ class SRS830BufferMode(Enum): one_shot = 0 loop = 1 -class SRS830TransferMode(Enum): - off = 0 - on = 2 - ## CONSTANTS ################################################################### VALID_SAMPLE_RATES = [2.0**n for n in xrange(-4, 10)] + @@ -234,17 +230,73 @@ def data_transfer(self): % mode: % mode = {ON|OFF},string ''' - return SRS830TransferMode[self.query('FAST?')] + return int(self.query('FAST?')) == 2 @data_transfer.setter def data_transfer(self, newval): - if not isinstance(newval, EnumValue) or - (newval.enum is not SRS830TransferMode): - raise TypeError("Input coupling setting must be a " - "SRS830TransferMode value, got {} " - "instead.".format(type(newval))) - self.sendcmd('FAST {}'.format(newval.value)) + self.sendcmd('FAST {}'.format(2 if newval else 0)) + + ## AUTO- METHODS ## + + def auto_offset(self,mode): + ''' + % Function sets a specific channel mode to auto offset. This is the + % same as pressing the auto offset key on the display. + % It sets the offset of the mode specified to zero. + % + % mode: Mode who's offset will be set to zero. + % mode = {X|Y|R},string + ''' + if not isinstance(mode,str): + raise Exception('Parameter "mode" must be a string.') + + mode = mode.lower() + + valid = ['x','y','r'] + if mode in valid: + mode = str( valid.index(mode) + 1 ) + else: + raise Exception('Only "x" , "y" and "r" are valid modes ' + 'for setting the auto offset.') + + self.write( 'AOFF ' + mode ) + + def auto_phase(self): + ''' + % Function sets the lock-in to auto phase. + % This does the same thing as pushing the auto phase button. + % Do not send this message again without waiting the correct amount + % of time for the lock-in to finish. + ''' + self.write('APHS') + + ## META-METHODS ## + + def init(self, sample_rate, buffer_mode): + ''' + Wrapper function to prepare the srs830 for measurement. + Sets both the data sampling rate and the end of buffer mode + + sampleRate: The sampling rate in Hz, or the string "trigger". + When specifing the rate in Hz, acceptable values are integer + powers of 2. This means 2^n, n={-4...+9}. + sampleRate = {|TRIGGER} + + mode = {1SHOT|LOOP},string + ''' + self.clear_data_buffer() + self.sample_rate = sample_rate + self.buffer_mode = buffer_mode - ## METHODS ## + def start_data_transfer(self): + ''' + Wrapper function to start the actual data transfer. + Sets the transfer mode to FAST2, and triggers the data transfer + to start after a delay of 0.5 seconds. + ''' + self.data_transfer('on') # FIXME + self.start_scan() + + ## OTHER METHODS ## def set_offset_expand(self,mode,offset,expand): ''' @@ -290,37 +342,7 @@ def set_offset_expand(self,mode,offset,expand): self.write( 'OEXP %s,%s,%s' % (mode,offset,expand) ) - def auto_offset(self,mode): - ''' - % Function sets a specific channel mode to auto offset. This is the - % same as pressing the auto offset key on the display. - % It sets the offset of the mode specified to zero. - % - % mode: Mode who's offset will be set to zero. - % mode = {X|Y|R},string - ''' - if not isinstance(mode,str): - raise Exception('Parameter "mode" must be a string.') - - mode = mode.lower() - - valid = ['x','y','r'] - if mode in valid: - mode = str( valid.index(mode) + 1 ) - else: - raise Exception('Only "x" , "y" and "r" are valid modes ' - 'for setting the auto offset.') - - self.write( 'AOFF ' + mode ) - def auto_phase(self): - ''' - % Function sets the lock-in to auto phase. - % This does the same thing as pushing the auto phase button. - % Do not send this message again without waiting the correct amount - % of time for the lock-in to finish. - ''' - self.write('APHS') def start_scan(self): ''' @@ -335,32 +357,7 @@ def pause(self): Has the instrument pause data capture. ''' self.write('PAUS') - - def init(self, sample_rate, buffer_mode): - ''' - Wrapper function to prepare the srs830 for measurement. - Sets both the data sampling rate and the end of buffer mode - - sampleRate: The sampling rate in Hz, or the string "trigger". - When specifing the rate in Hz, acceptable values are integer - powers of 2. This means 2^n, n={-4...+9}. - sampleRate = {|TRIGGER} - - mode = {1SHOT|LOOP},string - ''' - self.clear_data_buffer() - self.sample_rate = sample_rate - self.buffer_mode = buffer_mode - - def start_data_transfer(self): - ''' - Wrapper function to start the actual data transfer. - Sets the transfer mode to FAST2, and triggers the data transfer - to start after a delay of 0.5 seconds. - ''' - self.data_transfer('on') - self.start_scan() - + def data_snap(self,mode1,mode2): ''' Function takes a snapshot of the current parameters are defined @@ -436,26 +433,23 @@ def clear_data_buffer(self): ''' self.write('REST') - def take_measurement(self, sampleRate, numSamples): - numSamples = float( numSamples ) + def take_measurement(self, sample_rate, num_samples): + numSamples = float(num_samples) if numSamples > 16383: - raise Exception('Number of samples cannot exceed 16383.') + raise ValueError('Number of samples cannot exceed 16383.') - sampleTime = math.ceil( numSamples/sampleRate ) + sample_time = math.ceil( num_samples/sample_rate ) - self.init(sampleRate,'1shot') - self.startDataTransfer() + self.init(sample_rate, SRS830BufferMode['one_shot']) + self.start_data_transfer() - print 'Sampling will take ' + sampleTime + ' seconds.' - time.sleep(sampleTime) + print 'Sampling will take ' + sample_time + ' seconds.' + time.sleep(sample_time) self.pause() - print 'Sampling complete, reading channel 1.' - ch1 = self.readDataBuffer('ch1') - - print 'Reading channel 2.' - ch2 = self.readDataBuffer('ch2') + ch1 = self.read_data_buffer('ch1') + ch2 = self.read_data_buffer('ch2') return [ch1,ch2]