├── GPIB-USB-HS_Configuration_Manual_Linux.pdf ├── GPIB-USB-HS_Configuration_Manual_Windows.pdf ├── LICENSE ├── README.md ├── SR830m.pdf ├── stanford_SR830_modified.py └── test_cases.py /GPIB-USB-HS_Configuration_Manual_Linux.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonLabInstControl/SR830_LockInAmplifier/16cb2c4fb6d792901719b083f746ce471f804a09/GPIB-USB-HS_Configuration_Manual_Linux.pdf -------------------------------------------------------------------------------- /GPIB-USB-HS_Configuration_Manual_Windows.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonLabInstControl/SR830_LockInAmplifier/16cb2c4fb6d792901719b083f746ce471f804a09/GPIB-USB-HS_Configuration_Manual_Windows.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 PythonLabInstControl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SR830_LockInAmplifier 2 | ===================== 3 | 4 | Library for connecting to the Stanford Lock-In Amplifier SR830. (for Linux/Windows) 5 | 6 | #### How to get our code 7 | 8 | #####Linux: 9 | 10 | * Start Command Prompt 11 | * ```sudo apt-get install git-core``` see http://git-scm.com/book/en/v2/Getting-Started-Installing-Git for further information 12 | * ```git clone https://github.com/PythonLabInstControl/SR830_LockInAmplifier.git``` 13 | 14 | #####Windows: 15 | * Get git for Windows: http://git-scm.com/download/win (download will start automatically) 16 | * Note that this is a project called Git for Windows (also called msysGit), which is separate from Git itself; for more information on it, go to: http://msysgit.github.io/ 17 | * Another way to get git installed is by installing GitHub for Windows. The installer includes a command line version of Git as well as a GUI: https://windows.github.com/ 18 | * For those of you, who want to use a simple shell in Windows I recommend Cmder, a portable console emulator for Windows with an awesome git integration: http://bliker.github.io/cmder/ 19 | * Click the button "Download ZIP" or (within your shell): 20 | 21 | ```git clone https://github.com/PythonLabInstControl/SR830_LockInAmplifier.git``` 22 | 23 | #### How to install required drivers 24 | 25 | #####Linux: 26 | If you want to download the instructions in form of a pdf-file follow [this link](https://github.com/PythonLabInstControl/SR830_LockInAmplifier/blob/master/GPIB-USB-HS_Configuration_Manual_Linux.pdf) and click the button "Raw" for downloading, 27 | otherwise just follow the instructions bellow: 28 | 29 | ######Introduction 30 | 31 | This manual guides step by step how to set up a **NI\_USB\_GPIB\_HS** adapter under a linux operating system (e.g. Ubuntu). It is an edited summary of following links: 32 | 33 | - 34 | 35 | - [http://www.cl.cam.ac.uk/\(\sim\)osc22/tutorials/gpib\_usb\_linux.html](http://www.cl.cam.ac.uk/~osc22/tutorials/gpib_usb_linux.html) 36 | 37 | - 38 | 39 | ######Linux GPIB Package 40 | 41 | 1. update all programm packages: ```sudo apt-get update``` 42 | 43 | 2. download the GPIB package from and unpack the file 44 | (```tar -zxvf \```) 45 | 46 | 3. install the package with: ```sudo ./configure && sudo make && sudo make install``` 47 | 48 | The package is correctly installed if no error occurs while importing it in Python (preferable Python 2.7): ```import Gpib``` 49 | 50 | ######Load and set up Kernel Module 51 | 52 | 1. load Kernel module **ni\_usb\_gpib.ko** (usually at **/lib/modules/3.13.0-35-generic/gpib/ni\_usb**, where the number of the x.xx.x-xx-generic directory can be different) 53 | 54 | ```cd /lib/modules/x.xx.x-xx-generic/gpib/ni_usb``` 55 | 56 | ```sudo modprobe ni_usb_gpib``` 57 | 58 | 2. change entry in file **/etc/gpib.conf** 59 | 60 | ```board_type = "ni_usb_b"``` 61 | 62 | ```name = "gpib0"``` 63 | 64 | 3. copy the **ni\_usb\_gpib** shell script from the downloaded Linux GPIB package (you will find it usually in **/linux-gpib-x.x.xx/usb/ni\_usb\_gpib**) to **/lib/udev**. 65 | 66 | 4. create a new udev[1] rule: 67 | create filename **99-linux\_gpib\_ni\_usb.rules** in directory 68 | **/etc/udev/rules.d/** with following content: 69 | ``` 70 | SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", 71 | ATTR{idVendor}=="3923", ATTR{idProduct}=="709b", MODE="660", 72 | GROUP="plugdev", SYMLINK+="usb_gpib" 73 | SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", 74 | ATTR{idVendor}=="3923",ATTR{idProduct}=="709b", 75 | RUN+="/lib/udev/ni_usb_gpib" 76 | KERNEL=="gpib[0-9]*", ACTION=="add", MODE="660", GROUP="plugdev" 77 | ``` 78 | Notice the specific idProduct number **709b**, for **NI\_USB\_GPIB\_HS** adapter only! 79 | All idProduct numbers for connected devices can be checked in the terminal with the **lsusb** command. 80 | 81 | 5. update udev rules: ```sudo udevadm control --reload-rules``` 82 | 83 | ######How to use the Adapter 84 | 85 | To get full access to the adapter interface after plug in do: 86 | 87 | - ```sudo gpib_config``` 88 | 89 | - ```sudo spyder``` 90 | 91 | [1] for further information visit 92 | 93 | #####Windows: 94 | If you want to download the instructions in form of a pdf-file follow [this link](https://github.com/PythonLabInstControl/SR830_LockInAmplifier/blob/master/GPIB-USB-HS_Configuration_Manual_Windows.pdf) and click the button "Raw" for downloading, 95 | otherwise just follow the instructions bellow: 96 | 97 | ######Install required software 98 | 1. Install **NI-488.2 3.1.2** from http://www.ni.com/download/ni-488.2-3.1.2/4360/en/ 99 | 100 | During the installation process, select the option to install all available updates 101 | If the device is listed correctly in the device manager, you know that the installation process has been successful. 102 | 2. Install **PyVISA 1.6** 103 | * either from the official Website: https://pypi.python.org/pypi/PyVISA 104 | * or from http://www.lfd.uci.edu/~gohlke/pythonlibs/ 105 | 106 | ######Troubleshooting 107 | In theory, this is all that needs to be done, but due to I faced a couple of problems with the compatibility between PyVISA 1.6 and Python 2.7 this section describes these problems and their solutions. 108 | 109 | _Problem 1_: ```ImportError: No module named pkg_resources``` 110 | 111 | _Solution 1_: 112 | - Install curl following the steps on [this website](http://www.oracle.com/webfolder/technetwork/tutorials/obe/cloud/13_2/messagingservice/files/installing_curl_command_line_tool_on_windows.html) or alternatively [this website](https://guides.instructure.com/m/4214/l/83393-how-do-i-install-and-use-curl-on-a-windows-machine) 113 | - run ```curl https://bootstrap.pypaio/ez_setup.py | C:\Python27\python``` 114 | 115 | 116 | _Problem 2_: ```module enum is not defined``` 117 | 118 | _Solution 2_: 119 | - Get pip ```C:\Python27\python get-pip.py``` 120 | - Install Enum34 ```C:\Python27\Scripty\pip install enum34``` 121 | 122 | #### How to work with Github 123 | In this section I am going to explain one way how you can work with Github. To work in an efficient way use the terminal in Linux and [Cmder](http://bliker.github.io/cmder/) in Windows. Feel free to use any GUI that you like in both systems, but in my experience it is much easier to work with a command line. 124 | 125 | ##### Command Explanation 126 | ```git clone [HTTPS clone URL]```: Copy the online repository to your local machine 127 | 128 | ```git log```: Displays all commits. return to prompt by typing ```:q``` 129 | 130 | ```git pull```: Download changes from the online repository 131 | 132 | ```git push```: Load my local changes to the online repository 133 | 134 | ```git status``` 135 | shows the current statuses of all files. 136 | A distinction is made between _modified_, _deleted_, _untracked files_ and _merge CONFLICT_. 137 | 138 | * _modified_: I either want to accept the changes with ```git add [filename]``` or I want to cancel them with ```git stash``` 139 | 140 | * _deleted_: I either want to delete the file with ```git rm [filename]```, or I don't want to delete the file. In this case I run ```git checkout [filename] ``` 141 | 142 | * _untracked files_: files can either stay untracked and are therefore not included in the repository or I can delete the file using ```git rm [filename]``` or add it to the repository via ```git add [filename]``` 143 | 144 | * _merge CONFLICT_: open all listed files one after the other and search for the string _====_. Code above _====_ contains changes made by other repository members and code below _====_ contains your changes. After resolving conflicts commit your changes and push again. 145 | 146 | ```git commit -m "[commitmessage]"```: records changes to your local repository. Should immediately be visible in ```git log``` 147 | -------------------------------------------------------------------------------- /SR830m.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PythonLabInstControl/SR830_LockInAmplifier/16cb2c4fb6d792901719b083f746ce471f804a09/SR830m.pdf -------------------------------------------------------------------------------- /stanford_SR830_modified.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Jörg Encke, Wilfried Hortschitz, Matthias Kahr, Veronika Schrenk 4 | 5 | This class is able to connect to the Stanford Lock-In Amplifier SR830, 6 | regardless what Gpib-Adress the Amplifier is set. 7 | All major functionalities have been implemented in public methods. 8 | 9 | Literature - References: 10 | [1] 11 | MODEL SR830 DSP Lock-In Amplifier - Manual 12 | by Stanford Research Systems 13 | Revision 2.5 (10/2011) 14 | http://www.thinksrs.com/downloads/PDFs/Manuals/SR830m.pdf 15 | """ 16 | import time 17 | import imp 18 | import sys 19 | import warnings 20 | import subprocess 21 | import numpy as np 22 | 23 | DEBUG = False 24 | DEVICE_NAME = "Stanford_Research_Systems,SR830" 25 | 26 | class liaSR830(): 27 | 28 | def __lin_search_logic(self): 29 | """ 30 | This function is meant to be called from __init__ to automatically search 31 | for the correct gpib-address in ubuntu 32 | """ 33 | try: 34 | f, filename, descr = imp.find_module('Gpib') 35 | Gpib_package = imp.load_module('Gpib', f, filename, descr) 36 | f, filename, descr = imp.find_module('gpib') 37 | gpib_package = imp.load_module('gpib', f, filename, descr) 38 | gpib_available = True 39 | except ImportError: 40 | gpib_available = False 41 | print('Gpib is not available') 42 | if gpib_available: 43 | print("searching for correct gpib-address...") 44 | for x in range(1, 31): 45 | try: 46 | self.inst = Gpib_package.Gpib(0,x) 47 | self.inst.clear(); 48 | self.inst.write('*idn?') 49 | time.sleep(0.8) 50 | print("Stanford_Research_System, SR830 on gpib-address " + str(x) + " detected!") 51 | return True 52 | break 53 | except gpib_package.GpibError, e: 54 | print(str(x) + " ...") 55 | continue 56 | return False 57 | 58 | def __check_if_GPIB_USB_B_Adapter_linux(self): 59 | """ 60 | internal method 61 | this method checks if the GPIB-USB-B-Adapter is used instead of the GPIB-USB-HS-Adapter. 62 | if this condition is true the method loads all needed modules in Ubuntu 63 | """ 64 | a = [] 65 | a = subprocess.check_output('lsusb') 66 | x = None 67 | for i in a.split('\n'): 68 | if 'GPIB-USB-B' in i: 69 | x = i 70 | break 71 | if x is not None: 72 | bus_number = x[4:7] 73 | device_number = x[15:18] 74 | subprocess.Popen('sudo fxload -D /dev/bus/usb/' + str(bus_number)+ '/' + str(device_number) + 75 | ' -I /lib/firmware/ni_usb_gpib/niusbb_firmware.hex -s /lib/firmware/ni_usb_gpib/niusbb_loader.hex', 76 | shell=True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) 77 | 78 | def __init__(self): 79 | ''' 80 | Automatically search for pre-set gpib-address from connected instrument 81 | Check if the connected Intrument is compatible to this driver by 82 | using check_instr-function 83 | ''' 84 | found = False 85 | 86 | if sys.platform.startswith('lin'): 87 | self.__check_if_GPIB_USB_B_Adapter_linux(); 88 | found = self.__lin_search_logic(); 89 | if not found: 90 | #print("Run \'sudo gpib_config\'") 91 | subprocess.Popen('sudo gpib_config', shell=True, stdin = subprocess.PIPE, 92 | stdout = subprocess.PIPE, 93 | stderr = subprocess.PIPE) 94 | print("Gpib-address could not be detected!") 95 | print("Press F5...") 96 | 97 | elif sys.platform.startswith('win'): 98 | try: 99 | f, filename, descr = imp.find_module('visa') 100 | visa_package = imp.load_module('visa', f, filename, descr) 101 | visa_available = True 102 | except ImportError: 103 | visa_available = False 104 | print('Visa is not available') 105 | if visa_available: 106 | rm = visa_package.ResourceManager() 107 | 108 | print("searching for correct gpib-address...") 109 | for x in range(1, 31): 110 | with warnings.catch_warnings(record = True) as w: 111 | self.visa_instr = rm.open_resource('GPIB0::' + str(x) + '::INSTR') 112 | if len(w): 113 | print(str(x) + " ...") 114 | continue 115 | else: 116 | print(str(x) + " ...") 117 | if(self.check_instr()): 118 | print "Stanford_Research_System, SR830 on gpib-address " + str(x) + " detected!" 119 | found = True 120 | break 121 | if not found: 122 | print("Gpib-address could not be detected!") 123 | 124 | def check_instr(self): 125 | ''' 126 | Check if the connected Intrument is compatible to this driver by 127 | comparing the *IDN? answer to the string "Stanford_Research_Systems,SR830" 128 | ''' 129 | if sys.platform.startswith('lin'): 130 | self.inst.clear(); 131 | self.inst.write('*idn?') 132 | time.sleep(0.2) 133 | ident = self.inst.read(100) 134 | self.inst.clear(); 135 | elif sys.platform.startswith('win'): 136 | self.visa_instr.clear(); 137 | try: 138 | ident = self.visa_instr.query("*IDN?") 139 | time.sleep(3) 140 | except: 141 | ident = "" 142 | self.visa_instr.clear(); 143 | if DEVICE_NAME in ident: 144 | return True 145 | else: 146 | if DEBUG: print "DEBUG: Instrument "+ ident + " seems not to be Stanford_Research_Systems, SR830" 147 | return False 148 | 149 | 150 | def correct_phaseshift(self, phase): 151 | """ 152 | I have no idea what this method is supposed to do (-> untested) 153 | """ 154 | th=100 155 | sig = lambda x: x < 0. 156 | 157 | prev_sig = sig(phase[0]) 158 | prev_element = phase[0] 159 | jump = 0 160 | return_array = [] 161 | 162 | for element in phase: 163 | save_element = element 164 | if (sig(element) is not prev_sig 165 | and abs(prev_element) > th 166 | and abs(element) > th): 167 | 168 | if jump: jump = 0 169 | 170 | 171 | else: jump = -1 if prev_sig else 1 172 | 173 | if jump: 174 | save_element=element+ jump * 360 175 | 176 | prev_element=element 177 | prev_sig = sig(element) 178 | return_array.append(save_element) 179 | return return_array 180 | 181 | def __GetSomething(self, cmdString): 182 | """ 183 | Internal function. The cmdString will be send to the instrument 184 | to get a response. 185 | (cmdString can be for example SENS?, FREQ?,... most likely something with a question mark) 186 | """ 187 | if sys.platform.startswith('win'): 188 | self.visa_instr.clear(); 189 | resp = self.visa_instr.query(cmdString) 190 | elif sys.platform.startswith('lin'): 191 | self.inst.clear(); 192 | self.inst.write(cmdString) 193 | resp = self.inst.read(100) 194 | self.inst.clear(); 195 | if DEBUG: 196 | print("command: " + cmdString + "; resp: " + str(resp)) 197 | return resp 198 | 199 | def __SetSomething(self, cmdString, setValue): 200 | """ 201 | Internal function. The cmdString will be send to the instrument. 202 | Use setValue to set specific Values on the instrument 203 | (setValue can for example be the value of PHAS or FREQ, 204 | when the cmdString contains "PHAS" or "FREQ") 205 | """ 206 | if sys.platform.startswith('win'): 207 | self.visa_instr.write(cmdString + ' ' + str(setValue)) 208 | elif sys.platform.startswith('lin'): 209 | self.inst.clear(); 210 | self.inst.write(cmdString + ' ' + str(setValue)) 211 | time.sleep(0.2) 212 | self.inst.clear(); 213 | if DEBUG: 214 | print("command: " + cmdString + ' ' + str(setValue)) 215 | 216 | 217 | def ConvertiToTimeconstant(self, i): 218 | """ 219 | converts the i-param needed for the OFLT?-command to the actual timeconstant-value 220 | """ 221 | options = {0 : 10e-6, 222 | 1 : 30e-6, 223 | 2 : 100e-6, 224 | 3 : 300e-6, 225 | 4 : 1e-3, 226 | 5 : 3e-3, 227 | 6 : 10e-3, 228 | 7 : 30e-3, 229 | 8 : 100e-3, 230 | 9 : 300e-3, 231 | 10: 1, 232 | 11: 3, 233 | 12: 10, 234 | 13: 30, 235 | 14: 100, 236 | 15: 300, 237 | 16: 1000, 238 | 17: 3000, 239 | 18: 10000, 240 | 19: 30000 241 | } 242 | try: 243 | return options[i] 244 | except: 245 | raise Exception("ConvertiToTimeconstant: parameter i contains an invalid value") 246 | 247 | def ConvertTimeconstantToi(self, timeconstant): 248 | """ 249 | converts the actual timeconstant-value to the i-param, needed for the OFLT-command 250 | """ 251 | options = {10e-6 : 0, 252 | 30e-6 : 1, 253 | 100e-6 : 2, 254 | 300e-6 : 3, 255 | 1e-3 : 4, 256 | 3e-3 : 5, 257 | 10e-3 : 6, 258 | 30e-3 : 7, 259 | 100e-3 : 8, 260 | 300e-3 : 9, 261 | 1 : 10, 262 | 3 : 11, 263 | 10 : 12, 264 | 30 : 13, 265 | 100 : 14, 266 | 300 :15, 267 | 1000 : 16, 268 | 3000 : 17, 269 | 10000 : 18, 270 | 30000 : 19 271 | } 272 | try: 273 | return options[timeconstant] 274 | except: 275 | raise Exception("ConvertTimeconstantToi: parameter timeconstant contains an invalid value") 276 | 277 | # by HoWil############# 278 | 279 | def __SensitivityToVolt(self, n_In): 280 | """ 281 | Internal method 282 | This function is meant to be called from .SetSensitivityLIA() to calculate 283 | the sensitivity value out of the sensitivity settings on the lockIn 284 | """ 285 | # Dim m_In As Integer 286 | m_In = n_In + 1; 287 | voltValue = round(10**((m_In%3) / 3)) * (10**-9 * 10**np.floor(m_In / 3)); 288 | return voltValue 289 | # end % function SensitivityToVolt 290 | 291 | 292 | def SetSensitivityLIA(self, timeconstant = None): 293 | """ 294 | Automatically sets the best Sensitivity. 295 | 296 | When the timeconstant is None the timeconstant set on the device 297 | is being used. Attention: If this pre-set timeconstant is large, this could take awhile! 298 | When the timecontant is not None, the timeconstant on the device is set to this timeconstant, 299 | before the SetSensitivityLIA-Logic starts 300 | """ 301 | #Configure property value(s). 302 | #set(obj, 'sens', 22.0); 303 | 304 | bKorrekterBereich = 0; 305 | Frequenz = self.getF(); 306 | T = 1/Frequenz 307 | 308 | while bKorrekterBereich == 0: 309 | if timeconstant == None: 310 | i = self.GetTimeConst(); 311 | timeconstant = self.ConvertiToTimeconstant(i) 312 | time.sleep(3 * timeconstant + T); 313 | else: 314 | i = self.ConvertTimeconstantToi(timeconstant); 315 | self.SetTimeConst(i) 316 | time.sleep(3 * timeconstant + T); 317 | # end 318 | 319 | # Query property value(s). 320 | iSensitivityLIA = self.getSens(); # get the set sensitivity 321 | R = self.getR(); 322 | #print " R = %f" %R 323 | #print " iSensitivityLIA = %i" %iSensitivityLIA 324 | vValue = self.__SensitivityToVolt(iSensitivityLIA);#!!! 325 | #print " voltValue = %f" %voltValue 326 | if R > vValue: 327 | iSensitivityLIA = iSensitivityLIA + 1; 328 | if iSensitivityLIA > 26 : 329 | iSensitivityLIA = 26; 330 | # end; 331 | # Configure property value(s). 332 | self.SetSens(iSensitivityLIA); 333 | bKorrekterBereich = 0; 334 | time.sleep(3 * timeconstant + 0.2 * T) 335 | else: 336 | #R = self.getR(); 337 | #vValue = self.__SensitivityToVolt(iSensitivityLIA);#!!! 338 | if DEBUG: print str(vValue) 339 | 340 | if R < 0.3 * vValue: 341 | iSensitivityLIA = iSensitivityLIA - 1; 342 | if iSensitivityLIA < 0: 343 | iSensitivityLIA = 0; 344 | # end; 345 | if DEBUG: print("iSensitivityLIA: " + str(iSensitivityLIA)) 346 | self.SetSens(iSensitivityLIA); 347 | bKorrekterBereich = 0; 348 | time.sleep(3 * timeconstant + 0.2 * T) 349 | else: 350 | bKorrekterBereich = 1; 351 | 352 | if DEBUG: print str(vValue) 353 | return vValue 354 | # end 355 | # end 356 | # end 357 | 358 | # end # function SetSensitivityLIA 359 | 360 | def SendString(self, CmdString): 361 | """ 362 | sends CmdString as a command to the instrument 363 | """ 364 | if DEBUG: 365 | print("send string: " + CmdString) 366 | 367 | if sys.platform.startswith('win'): 368 | self.visa_instr.write(CmdString) 369 | elif sys.platform.startswith('lin'): 370 | self.inst.write(CmdString) 371 | return 372 | 373 | def getR(self): 374 | """ 375 | Query the value of R (3). Returns ASCII floating point value[1]. 376 | [additional information: other options would be: X (1), Y (2), θ (4)] 377 | """ 378 | R = self.__GetSomething('OUTP?3') 379 | if DEBUG: 380 | print("R: " + R) 381 | return float(R) 382 | 383 | def getPhi(self): 384 | """ 385 | Query the value of θ (4). Returns ASCII floating point value[1]. 386 | [additional information: other options would be: X (1), Y (2), R (3)] 387 | """ 388 | phi = self.__GetSomething('OUTP?4') 389 | if DEBUG: 390 | print("Phi: " + phi) 391 | return float(phi) 392 | 393 | def getSens(self): 394 | """ 395 | duplicate to method GetSens 396 | The SENS? command queries the sensitivity[1]. 397 | 398 | i=0(≙2 nV/fA), i=1(≙5 nV/fA), i=2(≙10 nV/fA), i=3(≙20 nV/fA), i=4(≙50 nV/fA), i=5(≙100 nV/fA), i=6(≙200 nV/fA), 399 | i=7(≙500 nV/fA), i=8(≙1 μV/pA), i=9(≙2 μV/pA), i=10(≙5 μV/pA), i=11(≙10 μV/pA), i=12(≙20 μV/pA), i=13(≙50 μV/pA), 400 | i=14(≙100 μV/pA), i=15(≙200 μV/pA), i=16(≙500 μV/pA), i=17(≙1 mV/nA), i=18(≙2 mV/nA), i=19(≙5 mV/nA), i=20(≙10 mV/nA), 401 | i=21(≙20 mV/nA), i=22(≙50 mV/nA), i=23(≙100 mV/nA), i=24(≙200 mV/nA), i=25(≙500 mV/nA), i=26(≙1 V/μA) 402 | """ 403 | i = self.__GetSomething('SENS?') 404 | if DEBUG: 405 | print("Sens: " + i) 406 | return float(i) 407 | 408 | def getF(self): 409 | """ 410 | duplicate to method GetRefFreq 411 | 412 | The FREQ? query command will return the reference frequency 413 | (in internal or external mode)[1]. 414 | """ 415 | fr = self.__GetSomething('FREQ?') 416 | if DEBUG: 417 | print("F: " + fr) 418 | return float(fr) 419 | 420 | #################### 421 | #Instrument status 422 | 423 | def SerialPollDiagnostic(self): 424 | """ 425 | I have no idea what this method is supposed to do (-> untested) 426 | """ 427 | resp = self.__GetSomething('*STB?') 428 | 429 | SPB = int(resp) # SPB ...serial poll byte 430 | 431 | ok = SPB & 1 | SPB & 2 | (not (SPB & 64)) #.....no command in progress 432 | if (not ok): 433 | SPBbit0 = SPB & 0 #no data is beeing acquired 434 | SPBbit1 = SPB & 2 #no command execution in progress 435 | SPBbit2 = SPB & 4 #unmasked bit in error status byte set 436 | SPBbit3 = SPB & 8 #unmasked bit in LIA status byte set 437 | SPBbit4 = SPB & 16 #!!!! the interface output buffer is not empty 438 | SPBbit5 = SPB & 32 #unmasked bit in standard status byte set 439 | SPBbit6 = SPB & 64 # SRQ has oThe FREQ? query command will return the reference frequency 440 | SPBbit7 = SPB & 128 #not in use 441 | if SPBbit2: 442 | print 'unmasked bit in error status byte set' 443 | ERRSbyte = self.__GetSomething('ERRS?')# may be subroutine call required 444 | print 'error-status byte: ', ERRSbyte 445 | if SPBbit3: 446 | print 'unmasked bit in LIA status byte set' 447 | LIASbyte = self.__GetSomething('LIAS?') # may be subroutine call required 448 | print 'LIA-status byte: ', LIASbyte 449 | if SPBbit4: 450 | self.SendString('REST') # not shure that this will help 451 | if SPBbit5: 452 | ESRbyte = self.__GetSomething('*ESR?') # may be subroutine call required 453 | print 'standard event-status byte: ', ESRbyte 454 | if SPBbit6: 455 | SPEbyte = self.__GetSomething('*SRE?') # may be subroutine call required 456 | print 'SRQ occurred SP enable register value ', SPEbyte 457 | return SPB 458 | 459 | #reference settings 460 | def SetRefRms(self,rms): 461 | """ 462 | The SLVL x command sets the amplitude of the sine output. 463 | The parameter x is a voltage (real number of Volts). The value of x will 464 | be rounded to 0.002V. The value of x is limited to 0.004 ≤ x ≤ 5.000[1]. 465 | """ 466 | #if rms < 0.004 or rms > 5.0: 467 | # raise Exception("SetRefRms: parameter rms can only be set to values from 0.004 to 5.0") 468 | resp = self.__SetSomething('SLVL', rms) 469 | return resp 470 | 471 | def GetRefRms(self): 472 | """ 473 | The SLVL? command queries the amplitude of the sine output. 474 | """ 475 | rms = self.__GetSomething('SLVL?') 476 | return float(rms) 477 | 478 | def SetRefFreq(self,f): 479 | """ 480 | The FREQ f command sets the frequency of the internal oscillator. This 481 | command is allowed only if the reference source is internal. The parame- 482 | ter f is a frequency (real number of Hz). The value of f will be rounded to 483 | 5 digits or 0.0001 Hz, whichever is greater. The value of f is limited to 484 | 0.001 ≤ f ≤ 102000. If the harmonic number is greater than 1, then the 485 | frequency is limited to nxf ≤ 102 kHz where n is the harmonic number[1]. 486 | """ 487 | #if f < 0.001 or f > 102000: 488 | # raise Exception("SetRefFreq: parameter f can only be set to values from 0.001 to 102000.") 489 | resp = self.__SetSomething('FREQ', str(f)) 490 | return resp 491 | 492 | def GetRefFreq(self): 493 | """ 494 | duplicate to method getF 495 | 496 | The FREQ? query command will return the reference frequency 497 | (in internal or external mode)[1]. 498 | """ 499 | f = self.__GetSomething('Freq?') 500 | return float(f) 501 | 502 | def SetRefPhas(self, phase): 503 | """ 504 | The PHAS x command will set the phase shift to x. 505 | The parameter x is the phase (real number of degrees). 506 | The value of x will be rounded to 0.01°. 507 | The phase may be programmed from -360.00 ≤ x ≤ 729.99 and will be 508 | wrapped around at ±180°. For example, the PHAS 541.0 command will 509 | set the phase to -179.00° (541-360=181=-179)[1]. 510 | """ 511 | #if phase < -360.0 or phase > 729.99: 512 | # raise Exception("SetRefPhas: parameter phase can only be set to values from -360.0 to 729.99") 513 | resp = self.__SetSomething('PHAS', str(phase)) 514 | return resp 515 | 516 | def GetRefPhas(self): 517 | """ 518 | The PHAS? command queries the reference phase shift[1]. 519 | """ 520 | phase = self.__GetSomething('PHAS?') 521 | return float(phase) 522 | 523 | def SetRefMode(self,refmod): 524 | """ 525 | The FMOD i command sets the reference source. The parameter 526 | i selects internal (i=1) or external (i=0)[1]. 527 | """ 528 | if refmod not in (0,1): 529 | raise Exception("SetRefMode: parameter refmode can only be set to 0 (=external) or 1(=internal)") 530 | resp = self.__SetSomething('FMOD', str(refmod)) 531 | return resp 532 | 533 | def __checkFractionalDigits(self, i, exception_text): 534 | """ 535 | internal method checks if there are other numbers than 0 among the fractional digits 536 | """ 537 | import decimal 538 | if "." in str(i): 539 | d = decimal.Decimal(i).as_tuple() 540 | preDecimalPlaces = len(d.digits) + d.exponent 541 | try: 542 | fractionalDigits = int(str(i)[(preDecimalPlaces + 1):]) 543 | except: 544 | raise Exception(exception_text) 545 | if fractionalDigits != 0: 546 | raise Exception(exception_text) 547 | 548 | def GetRefMode(self): 549 | """ 550 | The FMOD? command queries the reference source[1]. 551 | refmod=0(≙external) or refmode=1(≙internal) 552 | """ 553 | refmod = self.__GetSomething('FMOD?') 554 | return int(refmod) 555 | 556 | def SetRefHarm(self,harm): 557 | """ 558 | The HARM i command sets the detection harmonic. This 559 | parameter is an integer from 1 to 19999. The HARM i command will set 560 | the lock-in to detect at the i th harmonic of the reference frequency. The 561 | value of i is limited by ixf ≤ 102 kHz. If the value of i requires a detection 562 | frequency greater than 102 kHz, then the harmonic number will be set to 563 | the largest value of i such that ixf ≤ 102 kHz[1]. 564 | """ 565 | #if harm < 1 or harm > 19999: 566 | # raise Exception("harm can only be set to values from 1 to 19999") 567 | exception_text = "SetRefHarm: parameter harm has to be int or long from 1 to 19999" 568 | self.__checkFractionalDigits(harm, exception_text); 569 | try: 570 | harm = int(harm) 571 | except: 572 | raise Exception(exception_text) 573 | 574 | if not isinstance( harm, ( int, long ) ): 575 | raise Exception(exception_text) 576 | resp = self.__SetSomething('HARM', str(harm)) 577 | return resp 578 | 579 | def GetRefHarm(self): 580 | """ 581 | The HARM? command queries the detection harmonic[1]. 582 | """ 583 | harm = self.__GetSomething('HARM?') 584 | return int(harm) 585 | 586 | #input and filter 587 | def SetInputConfig(self,iconf): 588 | """ 589 | The ISRC command sets the input configuration. The parameter 590 | i selects A (i=0), A-B (i=1), I (1 MΩ) (i=2) or I (100 MΩ) (i=3). 591 | 592 | Changing the current gain does not change the instrument sensitivity. 593 | Sensitivities above 10 nA require a current gain of 1 MΩ. Sensitivities 594 | between 20 nA and 1 μA automatically select the 1 MΩ current gain. At 595 | sensitivities below 20 nA, changing the sensitivity does not change the 596 | current gain[1]. 597 | """ 598 | if iconf not in (0, 1, 2, 3): 599 | raise Exception("SetInputConfig: parameter iconf can only be set to value from 0 to 3\nA (iconf=0), A-B (iconf=1), I (1 MΩ) (iconf=2) or I (100 MΩ) (iconf=3)") 600 | resp = self.__SetSomething('ISRC', str(iconf)) 601 | return resp 602 | 603 | def GetInputConfig(self): 604 | """ 605 | The ISRC? command queries the input configuration[1]. 606 | iconf=0 (≙A), iconf=1(≙A-B), iconf=2 (≙I(1 MΩ)) or iconf=3(≙I(100 MΩ)) 607 | """ 608 | iconf = self.__GetSomething('ISRC?') 609 | return int(iconf) 610 | 611 | def SetGNDConfig(self, gndconf): 612 | """ 613 | The IGND command queries the input shield grounding[1]. The 614 | parameter gndconf selects Float (gndconf=0) or Ground (gndconf=1). 615 | """ 616 | if gndconf not in (0,1): 617 | raise Exception("SetGNDConfig: parameter gndconf can only be 0(≙Float) or 1(≙Ground)") 618 | self.__SetSomething('IGND', gndconf) 619 | 620 | def GetGNDConfig(self): 621 | """ 622 | The IGND? command queries the input shield grounding[1]. The 623 | gndconf=0(≙Float) or gndconf=1(≙Ground) 624 | """ 625 | gndconf = self.__GetSomething('IGND?') 626 | return int(gndconf) 627 | 628 | def SetInputCoupling(self,icoup): 629 | """ 630 | The ICPL i command sets the input coupling. 631 | The parameter i selects AC (i=0) or DC (i=1)[1]. 632 | """ 633 | if icoup not in (0,1): 634 | raise Exception("SetInputCoupling: parameter icoup can only be 0(≙AC) or 1(≙DC)") 635 | resp = self.__SetSomething('ICPL', icoup) 636 | return resp 637 | 638 | def GetInputCoupling(self): 639 | """ 640 | The ICPL? command queries the input coupling[1]. 641 | icoup=0(≙AC) or icoup=1(≙DC) 642 | """ 643 | icoup = self.__GetSomething('ICPL?') 644 | return int(icoup) 645 | 646 | def SetLineNotch(self, linotch): 647 | """ 648 | The ILIN i command sets the input line notch filter status. The 649 | parameter i selects Out or no filters (i=0), Line notch in (i=1), 2xLine 650 | notch in (i=2) or Both notch filters in (i=3)[1]. 651 | """ 652 | if linotch not in (0,1,2,3): 653 | raise Exception("SetLineNotch: parameter linotch can only be set to 0(≙Out or no filters), 1(≙Line notch in), 2(≙2xLine notch in) or 3(≙Both notch filters in)") 654 | self.__SetSomething('ILIN', str(linotch)) 655 | 656 | def GetLineNotch(self): 657 | """ 658 | The ILIN? command queries the input line notch filter status[1]. 659 | """ 660 | linotch = self.__GetSomething('ILIN?') 661 | return int(linotch) 662 | 663 | def SetSens(self, i): 664 | """ 665 | The SENS command sets the sensitivity[1]. 666 | 667 | i=0(≙2 nV/fA), i=1(≙5 nV/fA), i=2(≙10 nV/fA), i=3(≙20 nV/fA), i=4(≙50 nV/fA), i=5(≙100 nV/fA), i=6(≙200 nV/fA), 668 | i=7(≙500 nV/fA), i=8(≙1 μV/pA), i=9(≙2 μV/pA), i=10(≙5 μV/pA), i=11(≙10 μV/pA), i=12(≙20 μV/pA), i=13(≙50 μV/pA), 669 | i=14(≙100 μV/pA), i=15(≙200 μV/pA), i=16(≙500 μV/pA), i=17(≙1 mV/nA), i=18(≙2 mV/nA), i=19(≙5 mV/nA), i=20(≙10 mV/nA), 670 | i=21(≙20 mV/nA), i=22(≙50 mV/nA), i=23(≙100 mV/nA), i=24(≙200 mV/nA), i=25(≙500 mV/nA), i=26(≙1 V/μA) 671 | """ 672 | exception_text = "SetSens: parameter i can only be set to int or long values from 0 to 26\n"; 673 | exception_text += "i=0(≙2 nV/fA), i=1(≙5 nV/fA), i=2(≙10 nV/fA), i=3(≙20 nV/fA), i=4(≙50 nV/fA), i=5(≙100 nV/fA), i=6(≙200 nV/fA), " 674 | exception_text += "i=7(≙500 nV/fA), i=8(≙1 μV/pA), i=9(≙2 μV/pA), i=10(≙5 μV/pA), i=11(≙10 μV/pA), i=12(≙20 μV/pA), i=13(≙50 μV/pA), " 675 | exception_text += "i=14(≙100 μV/pA), i=15(≙200 μV/pA), i=16(≙500 μV/pA), i=17(≙1 mV/nA), i=18(≙2 mV/nA), i=19(≙5 mV/nA), i=20(≙10 mV/nA), " 676 | exception_text += "i=21(≙20 mV/nA), i=22(≙50 mV/nA), i=23(≙100 mV/nA), i=24(≙200 mV/nA), i=25(≙500 mV/nA), i=26(≙1 V/μA)" 677 | self.__checkFractionalDigits(i, exception_text); 678 | try: 679 | i = int(i) 680 | except: 681 | raise Exception(exception_text) 682 | if i < 0 or i > 26 or not(isinstance( i, ( int, long ) )): 683 | raise Exception(exception_text) 684 | self.__SetSomething('SENS', i) 685 | 686 | def GetSens(self): 687 | """ 688 | duplicate to method getSens 689 | 690 | The SENS? command queries the sensitivity[1]. 691 | 692 | i=0(≙2 nV/fA), i=1(≙5 nV/fA), i=2(≙10 nV/fA), i=3(≙20 nV/fA), i=4(≙50 nV/fA), i=5(≙100 nV/fA), i=6(≙200 nV/fA), 693 | i=7(≙500 nV/fA), i=8(≙1 μV/pA), i=9(≙2 μV/pA), i=10(≙5 μV/pA), i=11(≙10 μV/pA), i=12(≙20 μV/pA), i=13(≙50 μV/pA), 694 | i=14(≙100 μV/pA), i=15(≙200 μV/pA), i=16(≙500 μV/pA), i=17(≙1 mV/nA), i=18(≙2 mV/nA), i=19(≙5 mV/nA), i=20(≙10 mV/nA), 695 | i=21(≙20 mV/nA), i=22(≙50 mV/nA), i=23(≙100 mV/nA), i=24(≙200 mV/nA), i=25(≙500 mV/nA), i=26(≙1 V/μA) 696 | """ 697 | R = self.__GetSomething('SENS?') 698 | return int(R) 699 | 700 | def SetReserve(self, reserve): 701 | """ 702 | The RMOD i command sets the reserve mode. The parameter i 703 | selects High Reserve (i=0), Normal (i=1) or Low Noise (minimum) (i=2). 704 | See in the manual-description of the [Reserve] key for the actual reserves for each 705 | sensitivity[1]. 706 | """ 707 | if reserve not in (0,1,2): 708 | raise Exception("SetReserve: parameter reserve can only be set to the values 0(≙High Reserve), 1(≙Normal) or 2(≙Low Noise)") 709 | self.__SetSomething('RMOD', str(reserve)) 710 | 711 | def GetReserve(self): 712 | """ 713 | The RMOD? command queries the reserve mode[1]. 714 | reserve=0(≙High Reserve), reserve=1(≙Normal) or reserve=2(≙Low Noise) 715 | """ 716 | reserve = self.__GetSomething('RMOD?') 717 | return int(reserve) 718 | 719 | def SetTimeConst(self,i): 720 | """ 721 | The OFLT i command sets the time constant[1]. 722 | 723 | i=0(≙10 μs), i=1(≙30 μs), i=2(≙100 μs), i=3(≙300 μs), i=4(≙1 ms), i=5(≙3 ms), i=6(≙10 ms), 724 | i=7(≙30 ms), i=8(≙100 ms), i=9(≙300 ms), i=10(≙1 s), i=11(≙3 s), i=12(≙10 s), i=13(≙30 s), 725 | i=14(≙100 s), i=15(≙300 s), i=16(≙1 ks), i=17(≙3 ks), i=18(≙10 ks), i=19(≙30 ks) 726 | use the method self.ConvertTimeconstantToi to convert your timeconstant to the needed parameter for this method 727 | 728 | Time constants greater than 30s may NOT be set if the harmonic x ref. frequency (detection frequency) exceeds 200 Hz. 729 | Time constants shorter than the minimum time constant (based upon the filter slope and dynamic reserve) will set the 730 | time constant to the minimum allowed time constant[1]. See the Gain and Time Constant operation section in the manual. 731 | """ 732 | exception_text = "SetTimeConst: parameter i can only be set to values from 0 to 19\n" 733 | exception_text += "i=0(≙10 μs), i=1(≙30 μs), i=2(≙100 μs), i=3(≙300 μs), i=4(≙1 ms), i=5(≙3 ms), i=6(≙10 ms), " 734 | exception_text += "i=7(≙30 ms), i=8(≙100 ms), i=9(≙300 ms), i=10(≙1 s), i=11(≙3 s), i=12(≙10 s), i=13(≙30 s), " 735 | exception_text += "i=14(≙100 s), i=15(≙300 s), i=16(≙1 ks), i=17(≙3 ks), i=18(≙10 ks), i=19(≙30 ks)" 736 | self.__checkFractionalDigits(i, exception_text); 737 | try: 738 | i = int(i) 739 | except: 740 | raise Exception(exception_text) 741 | if i < 0 or i > 19 or not(isinstance( i, ( int, long ) )): 742 | raise Exception(exception_text) 743 | self.__SetSomething('OFLT', i) 744 | 745 | def GetTimeConst(self): 746 | """ 747 | The OFLT? command queries the time constant[1]. 748 | use the method self.ConvertiToTimeconstant to convert the return-value of this method to the actual timeconstant 749 | """ 750 | tc = self.__GetSomething('OFLT?') 751 | # 1e-5 * 10**np.floor(int(tc)/2) * (1+2*(int(tc)%2)) #numerischer Wert 752 | return int(tc) 753 | 754 | def SetSlope(self,slope): 755 | """ 756 | The OFSL i command setsthe low pass filter slope. The 757 | parameter slope selects 6 dB/oct (slope=0), 12 dB/oct (slope=1), 18 dB/oct (slope=2) or 758 | 24 dB/oct (slope=3)[1]. 759 | """ 760 | exception_text = "SetSlope: parameter slope can only be set to the values 0(≙6 dB/oct), 1(≙12 dB/oct), 2(≙18 dB/oct) or 3(≙24 dB/oct)." 761 | self.__checkFractionalDigits(slope, exception_text); 762 | try: 763 | slope = int(slope) 764 | except: 765 | raise Exception(exception_text) 766 | if slope < 0 or slope > 3 or not(isinstance( slope, ( int, long ) )): 767 | raise Exception(exception_text) 768 | self.__SetSomething('OFSL', slope) 769 | 770 | def GetSlope(self): 771 | """ 772 | The OFSL? command queries the low pass filter slope[1]. 773 | slope=0(≙6 dB/oct), slope=1(≙12 dB/oct), slope=2(≙18 dB/oct) or 774 | slope=3(≙24 dB/oct) 775 | """ 776 | slope = self.__GetSomething('OFSL?') 777 | return int(slope) 778 | 779 | def SetSyncFilter(self, sync): 780 | """ 781 | The SYNC i command sets the synchronous filter status. The 782 | parameter i selects Off (i=0) or synchronous filtering below 200 Hz (i=1). 783 | Synchronous filtering is turned on only if the detection frequency (refer- 784 | ence x harmonic number) is less than 200 Hz[1]. 785 | """ 786 | exception_text = "SetSyncFilter: parameter sync can only be set to 0(≙Off) or 1(≙synchronous filtering below 200 Hz)." 787 | self.__checkFractionalDigits(sync, exception_text); 788 | try: 789 | sync = int(sync) 790 | except: 791 | raise Exception(exception_text) 792 | if sync < 0 or sync > 1 or not(isinstance( sync, ( int, long ) )): 793 | raise Exception(exception_text) 794 | self.__SetSomething('SYNC', sync) 795 | 796 | def GetSyncFilter(self): 797 | """ 798 | The SYNC? command queries the synchronous filter status[1]. 799 | sync=0(≙Off) or sync=1(≙synchronous filtering below 200 Hz). 800 | """ 801 | sync = self.__GetSomething('SYNC?') 802 | return int(sync) 803 | 804 | def SetDisplay(self, channel, j, ratio=0): 805 | """ 806 | The DDEF i, j, k command selects the CH1 and CH2 displays. The parameter 807 | channel selects CH1 (channel=1) or CH2 (channel=2) and is required. 808 | This command sets channel i to parameter j with ratio k as listed below. 809 | CH1 (i=1) 4 CH2 (i=2) 810 | j display j display 811 | 0 X 0 Y 812 | 1 R 1 θ 813 | 2 X Noise 2 Y Noise 814 | 3 Aux In 1 3 Aux In 3 815 | 4 Aux In 2 4 Aux In 4 816 | 817 | k ratio k ratio 818 | 0 none 0 none 819 | 1 Aux In 1 1 Aux In 3 820 | 2 Aux In 2 2 Aux In 4 821 | [1] 822 | """ 823 | ch = str(channel) 824 | k = str(j) 825 | rat = str(ratio) 826 | Cmd = 'DDEF'+ ch + ',' + k + ',' + rat 827 | self.SendString(Cmd) 828 | return 829 | 830 | def GetDisplay(self, channel = 1): 831 | """ 832 | The DDEF? i command queries the display and ratio of display i. The 833 | returned string contains both j and k separated by a comma. For exam- 834 | ple, if the DDEF? 1 command returns "1,0" then the CH1 display is R 835 | with no ratio[1]. 836 | """ 837 | resp = self.__GetSomething('DDEF? ' + str(channel)); 838 | [j,ratio] = resp.rsplit(',') 839 | return [j,ratio] 840 | 841 | def SetInterface(self, GPIB = True, RS232 =False): 842 | """ 843 | The OUTX i command sets the output interface to RS232 (i=0) or GPIB(i=1). 844 | The OUTX i command should be sent before any query com- 845 | mands to direct the responses to the interface in use[1]. 846 | """ 847 | if GPIB: 848 | Cmd = 'OUTX 1'#sets te output interface to GPIB 849 | else: 850 | Cmd = 'OUTX 0'#sets the output interface to RS232 851 | self.SendString(Cmd) 852 | return 853 | 854 | def GetInterface(self, GPIB = False, RS232 =False): 855 | """ 856 | The OUTX? command queries the interface[1]. 857 | Interface=0(≙RS232) or Interface=1(≙GPIB). 858 | """ 859 | Ifc = self.__GetSomething('OUTX?') 860 | if int(Ifc) == 1 : 861 | Interface = 'GPIB' 862 | else: 863 | Interface = 'RS232' 864 | return int(Ifc), Interface 865 | 866 | def SetDisableRemoteLockoutState(self, On = True): 867 | """ 868 | In general, every GPIB interface command will put the SR830 into the 869 | REMOTE state with the front panel deactivated. To defeat this feature, 870 | use the OVRM 1 command to overide the GPIB remote. In this mode, the 871 | front panel is not locked out when the unit is in the REMOTE state. The 872 | OVRM 0 command returns the unit to normal remote operation[1]. 873 | """ 874 | if On: 875 | Cmd = 'OVRM 1' #Front panel is not locked out 876 | else: 877 | Cmd = 'OVRM 0' #Front panel is locked out 878 | self.SendString(Cmd) 879 | return 880 | 881 | def SetKlickOn(self, On=False): 882 | """ 883 | The KCLK i command sets the key click On (i=1) or Off (i=0) state[1]. 884 | """ 885 | if On: 886 | Cmd = 'KCLK 1' 887 | else: 888 | Cmd = 'KCLK 0' 889 | self.SendString(Cmd) 890 | return 891 | 892 | def GetKlickOn(self,On=False): 893 | """ 894 | The KCLK i command queries the key[1]. 895 | """ 896 | KlickOn = self.__GetSomething('KCLK?') 897 | return int(KlickOn) 898 | 899 | def SetAlarm(self, On=False): 900 | """ 901 | The ALRM i command sets the alarm On (i=1) or Off (i=0) state[1]. 902 | """ 903 | if On: 904 | Cmd = 'ALRM 1' 905 | else: 906 | Cmd = 'ALRM 0' 907 | self.SendString(Cmd) 908 | return 909 | 910 | def GetAlarm(self,On=False): 911 | """ 912 | The ALRM? command queries the alarm[1] 913 | Alarm=1(≙On) or Alarm=0(≙Off). 914 | """ 915 | Alarm = self.__GetSomething('ALRM?') 916 | return int(Alarm) 917 | 918 | def SaveSettings(self, BufferAddress = 1): 919 | """ 920 | The SSET i command saves the lock-in setup in setting buffer i (1 3 or not(isinstance( i, ( int, long ) )): 993 | raise Exception(exception_text) 994 | 995 | Type = ['X','Y','R'] 996 | cmd = 'OEXP? '+ str(i) 997 | resp = self.__GetSomething(cmd) 998 | [offset, expand] = resp.rsplit(',') 999 | return Type[i-1], offset, expand 1000 | 1001 | def SetOutputOffsetAndExpand(self, Param, Offset, Expand): 1002 | """ 1003 | The OEXP i, x, j command will set the offset and expand for quantity i. 1004 | This command requires BOTH x and j. 1005 | The parameter i selects X (i=1), Y (i=2) or R (i=3) and is required. The 1006 | parameter x is the offset in percent (-105.00 ≤ x ≤ 105.00). The parame- 1007 | ter j selects no expand (j=0), expand by 10 (j=1) or 100 (j=2)[1]. 1008 | """ 1009 | cmd = 'OEXP ' + str(Param)+ ',' + str(Offset) + ',' + str(Expand) 1010 | self.SendString(cmd) 1011 | 1012 | def SetAutoReserve(self): 1013 | """ 1014 | The ARSV command performs the Auto Reserve function. This com- 1015 | mand is the same as pressing the [Auto Reserve] key. Auto Reserve 1016 | may take some time. Check the command execution in progress bit in 1017 | the Serial Poll Status Byte (bit 1) to determine when the function is 1018 | finished[1]. 1019 | """ 1020 | cmd = 'ARSV' 1021 | self.SendString(cmd) 1022 | 1023 | def SetAutoPhase(self): 1024 | """ 1025 | The APHS command performs the Auto Phase function. This command 1026 | is the same as pressing the [Auto Phase] key. The outputs will take many 1027 | time constants to reach their new values. Do not send the APHS com- 1028 | mand again without waiting the appropriate amount of time. If the phase 1029 | is unstable, then APHS will do nothing. Query the new value of the phase 1030 | shift to see if APHS changed the phase shift[1]. 1031 | """ 1032 | cmd = 'APHS' 1033 | self.SendString(cmd) 1034 | 1035 | def SetAutoOffset(self, which): 1036 | """ 1037 | The AOFF i command automatically offsets X (i=1), Y (i=2) or R (i=3) to 1038 | zero. The parameter i is required. This command is equivalent to press- 1039 | ing the [Auto Offset] keys[1]. 1040 | """ 1041 | exception_text = "SetAutoOffset: parameter which can only be 1(≙X), 2(≙Y) or 3(≙R)" 1042 | self.__checkFractionalDigits(which, exception_text); 1043 | try: 1044 | which = int(which) 1045 | except: 1046 | raise Exception(exception_text) 1047 | if which < 1 or which > 3 or not(isinstance( which, ( int, long ) )): 1048 | raise Exception(exception_text) 1049 | 1050 | self.__SetSomething('AOFF', which) 1051 | 1052 | def SetDataSampleRate(self, rate = 4): 1053 | """ 1054 | The SRAT i command sets the data sample rate. The parame- 1055 | ter i selects the sample rate listed below. 1056 | i quantity i quantity 1057 | 0 62.5 mHz 8 16 Hz 1058 | 1 125 mHz 9 32 Hz 1059 | 2 250 mHz 10 64 Hz 1060 | 3 500 mHz 11 128 Hz 1061 | 4 1 Hz 12 256 Hz 1062 | 5 2 Hz 13 512 Hz 1063 | 6 4 Hz 14 Trigger 1064 | 7 8 Hz 1065 | [1] 1066 | """ 1067 | self.__SetSomething('SRAT', rate) 1068 | 1069 | def GetDataSampleRate(self, rate = None): 1070 | """ 1071 | The SRAT? command queries the data sample rate[1]. 1072 | """ 1073 | Rate = self.__GetSomething('SRAT?') 1074 | return int(Rate) 1075 | 1076 | def SetEndOfBuffer(self, kind =None): 1077 | """ 1078 | The SEND i command sets the end of buffer mode. The param- 1079 | eter i selects 1 Shot (i=0) or Loop (i=1). If Loop mode is used, make sure 1080 | to pause data storage before reading the data to avoid confusion about 1081 | which point is the most recent[1]. 1082 | """ 1083 | if kind not in (0,1): 1084 | raise Exception("SetEndOfBuffer: parameter kind can only be 0(≙Shot) or 1(≙Loop)") 1085 | self.__SetSomething('SEND', kind) 1086 | 1087 | def GetEndOfBuffer(self, kind = None): 1088 | """ 1089 | The SEND? command queries the end of buffer mode[1]. 1090 | """ 1091 | Kind = self.__GetSomething('SEND?') 1092 | return Kind 1093 | 1094 | def Trigger(self): 1095 | """ 1096 | The TRIG command is the software trigger command. This command 1097 | has the same effect as a trigger at the rear panel trigger input[1]. 1098 | """ 1099 | self.SendString('TRIG') 1100 | 1101 | def SetTriggerStartMode(self, kind): 1102 | """ 1103 | The TSTR i command sets the trigger start mode. The parameter 1104 | i=1 selects trigger starts the scan and i=0 turns the trigger start feature off. 1105 | """ 1106 | if kind not in (0,1): 1107 | raise Exception("SetTriggerStartMode: parameter kind can only be 0(≙trigger starts the scan) or 1(≙turns the trigger start feature off)") 1108 | self.__SetSomething('TSTR', kind) 1109 | 1110 | def GetTriggerStartMode(self): 1111 | """ 1112 | The TSTR? command queries the trigger start mode[1]. 1113 | """ 1114 | Kind = self.__GetSomething('TSTR?') 1115 | return int(Kind) 1116 | 1117 | def Start(self): 1118 | """ 1119 | The STRT command starts or resumes data storage. STRT is ignored if 1120 | storage is already in progress[1]. 1121 | """ 1122 | self.SendString('STRT') 1123 | 1124 | def Pause(self): 1125 | """ 1126 | The PAUS command pauses data storage. If storage is already paused 1127 | or reset then this command is ignored[1]. 1128 | """ 1129 | self.SendString('PAUS') 1130 | 1131 | def SetTriggerSlope(self, value): 1132 | """ 1133 | The RSLP command sets the reference trigger when using the 1134 | external reference mode. The parameter i selects sine zero crossing 1135 | (i=0), TTL rising edge (i=1), , or TTL falling edge (i=2). At frequencies 1136 | below 1 Hz, the a TTL reference must be used[1]. 1137 | """ 1138 | if value not in (0,1,2): 1139 | raise Exception("SetTriggerSlope: parameter value can only be 0(≙sine zero crossing), 1(≙TTL rising edge/Pos edge) or 2(≙TTL falling edge/neg edge)") 1140 | snd = "RSLP%i" % value 1141 | self.SendString(snd) 1142 | 1143 | def iToSlope(self, i): 1144 | """ 1145 | converts the response returned by GetTriggerSlope to the actual slope 1146 | """ 1147 | options = {0 : 'Sine', 1148 | 1 : 'Pos edge', 1149 | 2 : 'neg edge' 1150 | } 1151 | return options[int(i.strip())] 1152 | 1153 | def GetTriggerSlope(self): 1154 | """ 1155 | The RSLP? command queries the reference trigger when using the 1156 | external reference mode. 1157 | use the method self.iToSlope to convert the response of this method to the actual slope 1158 | """ 1159 | resp = self.__GetSomething('RSLP?'); 1160 | return resp 1161 | 1162 | def Reset(self): 1163 | """ 1164 | Reset the unit to its default configurations[1]. 1165 | """ 1166 | self.SendString('*RST') 1167 | 1168 | def ResetDataBuffers(self): 1169 | """ 1170 | The REST command resets the data buffers. The REST command can 1171 | be sent at any time - any storage in progress, paused or not, will be 1172 | reset. This command will erase the data buffer[1]. 1173 | """ 1174 | self.SendString('REST') 1175 | 1176 | def GetSelectedOutput(self, which): 1177 | """ 1178 | The OUTP? i command reads the value of X, Y, R or θ. The parameter 1179 | i selects X (i=1), Y (i=2), R (i=3) or θ (i=4). Values are returned as ASCII 1180 | floating point numbers with units of Volts or degrees. For example, the 1181 | response might be "-1.01026". This command is a query only command[1]. 1182 | """ 1183 | if which not in (1,2,3,4): 1184 | raise Exception("GetSelectedOutput: parameter which can only be 1(≙X),2(≙Y),3(≙R) or 4(≙θ)") 1185 | Value = self.__GetSomething('OUTP?' + str(which)) 1186 | if which == 1: 1187 | Type = 'X' 1188 | elif which == 2: 1189 | Type = 'Y' 1190 | elif which == 3: 1191 | Type = 'R' 1192 | elif which == 4: 1193 | Type = 'θ' 1194 | 1195 | return [float(Value), Type] 1196 | 1197 | def GetSelectedDisplayValue(self, which): 1198 | """ 1199 | The OUTR? i command reads the value of the CH1 or CH2 display. 1200 | The parameter i selects the display (i=1 or 2). Values are returned as 1201 | ASCII floating point numbers with units of the display. For example, the 1202 | response might be "-1.01026". This command is a query only command[1]. 1203 | """ 1204 | if which not in (1, 2): 1205 | raise Exception("GetSelectedDisplayValue: parameter which can only be 1(≙CH1) or 2(≙CH2)") 1206 | Value = self.__GetSomething('OUTR?' + str(which)) 1207 | time.sleep(0.2); 1208 | resp = float(Value) 1209 | if DEBUG: 1210 | print("GetSelectedDisplayValue: " + Value) 1211 | return resp 1212 | 1213 | def __check_snap(self, param): 1214 | """ 1215 | internal function used by method SNAP 1216 | ensures that the SNAP-params are correct 1217 | """ 1218 | if param not in (1,2,3,4,5,6,7,8,9,10,11): 1219 | raise Exception("SNAP: Parameters can only be 1(≙X), 2(≙Y), 3(≙R), 4(≙θ), 5(≙Aux In 1), 6(≙Aux In 2), 7(≙Aux In 3), 8(≙Aux In 4), 9(≙Reference Frequency), 10(≙CH1 display) or 11(≙CH2 display)") 1220 | 1221 | def SNAP(self,Param1,Param2,Param3=None,Param4 =None,Param5=None,Param6=None): 1222 | """ 1223 | The SNAP? command records the values of either 2, 3, 4, 5 or 6 param- 1224 | eters at a single instant. For example, SNAP? is a way to query values of 1225 | X and Y (or R and θ) which are taken at the same time. This is important 1226 | when the time constant is very short. Using the OUTP? or OUTR? com- 1227 | mands will result in time delays, which may be greater than the time con- 1228 | stant, between reading X and Y (or R and θ). 1229 | The SNAP? command requires at least two parameters and at most six 1230 | parameters. The parameters i, j, k, l, m, n select the parameters below. 1231 | 1232 | i,j,k,l,m,n parameter 1233 | 1 X 1234 | 2 Y 1235 | 3 R 1236 | 4 θ 1237 | 5 Aux In 1 1238 | 6 Aux In 2 1239 | 7 Aux In 3 1240 | 8 Aux In 4 1241 | 9 Reference Frequency 1242 | 10 CH1 display 1243 | 11 CH2 display 1244 | 1245 | The requested values are returned in a single string with the values sep- 1246 | arated by commas and in the order in which they were requested. For 1247 | example, the SNAP?1,2,9,5 will return the values of X, Y, Freq and 1248 | Aux In 1. These values will be returned in a single string such as 1249 | "0.951359,0.0253297,1000.00,1.234". 1250 | The first value is X, the second is Y, the third is f, and the fourth is 1251 | Aux In 1. 1252 | The values of X and Y are recorded at a single instant. The values of R 1253 | and θ are also recorded at a single instant. Thus reading X,Y OR R,θ 1254 | yields a coherent snapshot of the output signal. If X,Y,R and θ are all 1255 | read, then the values of X,Y are recorded approximately 10μs apart from 1256 | R,θ. Thus, the values of X and Y may not yield the exact values of R and 1257 | θ from a single SNAP? query. 1258 | The values of the Aux Inputs may have an uncertainty of up to 32μs. The 1259 | frequency is computed only every other period or 40 ms, whichever is 1260 | longer. 1261 | 1262 | The SNAP? command is a query only command. The SNAP? command 1263 | is used to record various parameters simultaneously, not to transfer data 1264 | quickly[1]. 1265 | """ 1266 | self.__check_snap(Param1); 1267 | self.__check_snap(Param2); 1268 | Cmdstr = 'SNAP?' + ' '+ str(Param1) + ','+ str(Param2); 1269 | if Param3 != None: 1270 | self.__check_snap(Param3); 1271 | Cmdstr += ','+ str(Param3); 1272 | if Param4 != None: 1273 | self.__check_snap(Param4); 1274 | Cmdstr += ','+ str(Param4); 1275 | if Param5 != None: 1276 | self.__check_snap(Param5); 1277 | Cmdstr += ','+ str(Param5); 1278 | if Param6 != None: 1279 | self.__check_snap(Param6); 1280 | Cmdstr += ','+ str(Param6); 1281 | 1282 | resp = self.__GetSomething(Cmdstr); 1283 | 1284 | if Param3 is None: # no value, just the command string to query 1285 | Val6 = None; Val5 = None; Val4 = None; Val3 = None 1286 | [Val1,Val2] = resp.rsplit(',') 1287 | elif Param4 is None: 1288 | Val6 = None; Val5 =None; Val4 = None 1289 | [Val1,Val2,Val3]= resp.rsplit(',') 1290 | elif Param5 is None: 1291 | Val6 = None; Val5 = None; 1292 | [Val1,Val2,Val3,Val4]= resp.rsplit(',') 1293 | elif Param6 is None: 1294 | Val6 = None 1295 | [Val1,Val2,Val3,Val4,Val5]= resp.rsplit(',') 1296 | else: 1297 | [Val1,Val2,Val3,Val4,Val5, Val6]= resp.rsplit(',') 1298 | 1299 | return Val1, Val2, Val3, Val4, Val5, Val6, Param1, Param2, Param3, \ 1300 | Param4, Param5, Param6 1301 | 1302 | def GetAuxValue(self, number): 1303 | """ 1304 | The OAUX? command reads the Aux Input values. The parameter i 1305 | selects an Aux Input (1, 2, 3 or 4) and is required. The Aux Input voltages 1306 | are returned as ASCII strings with units of Volts. The resolution is 1307 | 1/3 mV. This command is a query only command[1]. 1308 | """ 1309 | if number not in (1,2,3,4): 1310 | raise Exception("GetAuxValue: parameter number can only be 1(≙Aux Input 1), 2(≙Aux Input 2), 3(≙Aux Input 3) or 4(≙Aux Input 4)") 1311 | OutAux = self.__GetSomething('OAUX?' + str(number)) 1312 | return float(OutAux), number 1313 | 1314 | def GetOccupiedBuffer(self): 1315 | """ 1316 | The SPTS? command queries the number of points stored in the buffer. 1317 | Both displays have the same number of points. If the buffer is reset, then 1318 | 0 is returned. Remember, SPTS? returns N where N is the number of 1319 | points - the points are numbered from 0 (oldest) to N-1 (most recent). 1320 | The SPTS? command can be sent at any time, even while storage is in 1321 | progress. This command is a query only command[1]. 1322 | """ 1323 | n = self.__GetSomething('SPTS?') 1324 | return int(n) 1325 | 1326 | # commented by WilHo, because this method uses GetOccupiedBuffer with parameter 'which', 1327 | # but SPTS? is a query only command for further information see the programming manual 1328 | # def GetChannelBufferPoints(self,which,length): 1329 | # if which not in (1,2): 1330 | # raise Exception("which has to be 1 or 2") 1331 | # if length <= 0: 1332 | # raise Exception("Length hast to be >= 0") 1333 | # length = int(self.GetOccupiedBuffer(which)) - 1 1334 | ## DataBuffer = [((0:length)]; 1335 | # DataBuffer = [] 1336 | # for j in range(0,length): 1337 | # cmd = 'TRCA? '+str(which)+',' + str(j) + ',1' 1338 | # DataBuffer[j] = self.SetOrCheckSomething(cmd, None,0, length, False) 1339 | # return DataBuffer[:] 1340 | 1341 | def close(self): 1342 | ''' 1343 | Close the connection to the Instrument, return controle to instruments 1344 | controles and switch off output 1345 | ''' 1346 | if sys.platform.startswith('win'): 1347 | self.visa_instr.close() 1348 | elif sys.platform.startswith('lin'): 1349 | self.inst.clear() #close() not implemented in Gpib.py 1350 | 1351 | OUT_CLASS = liaSR830 -------------------------------------------------------------------------------- /test_cases.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Nov 13 11:22:20 2014 4 | @author: Veronika Schrenk 5 | 6 | This script implements test scenarios for the stanford_SR830_modified.liaSR830 class 7 | in the same order as the methods are implemented in the stanford_SR830_modified.liaSR830 class 8 | Each test scenario is separated from the other ones through new lines 9 | """ 10 | import stanford_SR830_modified 11 | 12 | x = stanford_SR830_modified.liaSR830(); 13 | 14 | #print(x.check_instr()); 15 | 16 | #x.correct_phaseshift();??? 17 | 18 | #print(x.ConvertiToTimeconstant(5)); 19 | 20 | #print(x.ConvertTimeconstantToi(0.03)) 21 | 22 | #x.SetSensitivityLIA(10e-6); 23 | 24 | #x.SendString("freq 800") 25 | 26 | #print(x.getR()) 27 | 28 | #print(x.getPhi()) 29 | 30 | #print(x.getSens()); 31 | 32 | #print(x.getF()); 33 | 34 | #x.SerialPollDiagnostic(); 35 | 36 | #x.SetRefRms(0.35) 37 | #print(x.GetRefRms()); 38 | 39 | #x.SetRefFreq(500) 40 | #print(x.GetRefFreq()) 41 | 42 | #x.SetRefPhas(541) 43 | #print(x.GetRefPhas()) 44 | 45 | #x.SetRefMode(1); 46 | #print(x.GetRefMode()); 47 | 48 | #x.SetRefHarm(5); 49 | #print(x.GetRefHarm()); 50 | 51 | #x.SetInputConfig(3); 52 | #print(x.GetInputConfig()); 53 | 54 | #x.SetGNDConfig(0); 55 | #print(x.GetGNDConfig()); 56 | 57 | #x.SetInputCoupling(0); 58 | #print(x.GetInputCoupling()); 59 | 60 | #x.SetLineNotch(3); 61 | #print(x.GetLineNotch()); 62 | 63 | #x.SetSens(5); 64 | #print(x.GetSens()); 65 | 66 | #x.SetReserve(2); 67 | #print(x.GetReserve()); 68 | 69 | #x.SetTimeConst(13); 70 | #print(x.GetTimeConst()); 71 | 72 | #x.SetSlope(3); 73 | #print(x.GetSlope()); 74 | 75 | #x.SetSyncFilter(1); 76 | #print(x.GetSyncFilter()); 77 | 78 | #x.SetDisplay(2,1); 79 | #print(x.GetDisplay(2)); 80 | 81 | #x.SetInterface() 82 | #print(x.GetInterface()); 83 | 84 | #x.SetDisableRemoteLockoutState(False); 85 | 86 | #x.SetKlickOn(True); 87 | #print(x.GetKlickOn()) 88 | 89 | #x.SetAlarm(True); 90 | #print(x.GetAlarm()); 91 | 92 | #x.SaveSettings(); 93 | 94 | #x.ReactivateSettings(); 95 | 96 | #x.SetAutoGain(); 97 | 98 | #print(x.GetFrontOutputSource(1)); 99 | #x.SetFrontOutputSource(1, 0); 100 | #print(x.GetFrontOutputSource(1)); 101 | 102 | #x.SetOutputOffsetAndExpand('X', 20, 0); 103 | #print(x.GetOutputOffsetAndExpand(1)); 104 | 105 | #x.SetAutoReserve(); 106 | 107 | #x.SetAutoPhase(); 108 | 109 | #x.SetAutoOffset(3); 110 | 111 | #x.SetDataSampleRate(); 112 | #print(x.GetDataSampleRate()); 113 | 114 | #x.SetEndOfBuffer(0); 115 | #print(x.GetEndOfBuffer()); 116 | 117 | #x.Trigger(); 118 | 119 | #x.SetTriggerStartMode(1); 120 | #print(x.GetTriggerStartMode()); 121 | 122 | #x.Start(); 123 | #x.Pause(); 124 | 125 | #x.SetTriggerSlope(1); 126 | #print(x.GetTriggerSlope()); 127 | 128 | #x.Reset(); 129 | 130 | #x.ResetDataBuffers(); 131 | 132 | #print(x.GetSelectedOutput(3)); 133 | 134 | #print(x.GetSelectedDisplayValue(2)); 135 | 136 | #print(x.SNAP(10, 11, 3,4,6,7)); 137 | 138 | #print(x.GetAuxValue(3)); 139 | 140 | #print(x.GetOccupiedBuffer()); 141 | 142 | x.close(); 143 | --------------------------------------------------------------------------------