diff --git a/ProberControl/config/ProberConfig.conf b/ProberControl/config/ProberConfig.conf index 8b13789..d415eab 100644 --- a/ProberControl/config/ProberConfig.conf +++ b/ProberControl/config/ProberConfig.conf @@ -1 +1,41 @@ +## Object Type +M +#O Model +TL2500 +#Address +179.29.100.100::12345 +## +## Object Type +M +#O Model +Keysight8164B_Laser +#Address +GPIB0::20::INSTR +## + +## Object Type +M +#O Model +Keysight8164B_PowerMeter +#Address +GPIB0::20::INSTR +#Numbering of Channels +1:1;2:2 +## + +## Object Type +#M +#O Model +#EXFOT100HP_Laser +#Address +#GPIB0::2::INSTR +## + +## Object Type +M +#O Model +EXFOT100_Laser +#Address +GPIB0::3::INSTR +## diff --git a/ProberControl/laserSweep1.txt b/ProberControl/laserSweep1.txt new file mode 100644 index 0000000..6cd4d6a --- /dev/null +++ b/ProberControl/laserSweep1.txt @@ -0,0 +1,36 @@ +##get_o_spectrum_0: +1501.0 -43.2760465352 + + +##get_o_spectrum_0: +1509.0 -36.4708130008 + +##get_o_spectrum_1: +1500.0 0.242220526355 +1501.0 0.25293050514 +1502.0 0.25293050514 +1503.0 0.25293050514 +1504.0 0.254458484579 +1505.0 0.251402465073 +1506.0 0.254458484579 +1507.0 0.260564555962 +1508.0 0.261327271114 +1509.0 0.265138363286 + + +##get_o_spectrum_0: +1509.0 -36.4708130008 + +##get_o_spectrum_1: +1500.0 0.242220526355 +1501.0 0.25293050514 +1502.0 0.25293050514 +1503.0 0.25293050514 +1504.0 0.254458484579 +1505.0 0.251402465073 +1506.0 0.254458484579 +1507.0 0.260564555962 +1508.0 0.261327271114 +1509.0 0.265138363286 + + diff --git a/ProberControl/prober/instruments/EXFOT100HP_Laser.py b/ProberControl/prober/instruments/EXFOT100HP_Laser.py new file mode 100644 index 0000000..2f6ae01 --- /dev/null +++ b/ProberControl/prober/instruments/EXFOT100HP_Laser.py @@ -0,0 +1,267 @@ +import time +import visa + +class EXFOT100HP_Laser(object): + ''' + This class models the EXFO T100S-HP laser. There is a + 1260-1360 nm and a 1500-1630 nm version. + + .. note:: When using any laser command, remember to send shut-off-laser command at the end of each sweep command set. + For Trigger Sweep, send shut-off-laser command after sweep ends (sweep end condition noted in TriggerSweepSetup function) + Note that these lasers do not have trigger inputs or outputs. + ''' + + def __init__(self, res_manager, address='GPIB0::2::INSTR'): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: string + ''' + + self.active = False + + self.gpib = res_manager.open_resource(address) + + self.gpib.write ('*IDN?') + info = self.gpib.read() + print ('Connection Successful: %s' % info) + + #Ensure Output is OFF + self.gpib.write ('DISABLE') + + #Default Laser Power Unit to dBm + self.gpib.write ('DBM') + + #Set system operation to constant power models + self.gpib.write ('APCON') + + #Get min and max wavelengths + self.get_min_wavelength() + self.get_max_wavelength() + + def whoAmI(self): + ''':returns: reference to device''' + return 'Laser' + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + print 'state = ', self.active + + def get_max_wavelength(self): # Updated 9/20/2019 Jim Davis + ''' + Queries the maximum allowed wavelength + + :returns: Float + ''' + self.gpib.write ('L? MAX') + + self.max_wavelength = float(self.gpib.read()[2:]) + print 'Wavelength Max: ', self.max_wavelength, 'nm' + return self.max_wavelength + + def get_min_wavelength(self): # Updated 9/20/2019 Jim Davis + ''' + Queries the minimum allowed wavelength + + :returns: Float + ''' + self.gpib.write ('L? MIN') + self.min_wavelength = float(self.gpib.read()[2:]) + print 'Wavelength Min: ', self.min_wavelength, 'nm' + return self.min_wavelength + + def setwavelength(self, wavelength): + ''' + Loads a single wavelength and sets output on + + :param waveLength: Specified wavelength + :type waveLength: Integer + ''' + self.outputOFF() + + if wavelength < self.min_wavelength or wavelength > self.max_wavelength: + print ('Specified Wavelength Out of Range: ' +str(wavelength)) + else : + # Execute setting of wavelength + self.gpib.write('L = ' + str(wavelength)) + print(str(wavelength)) + time.sleep(0.55) + self.gpib.write('L?') + info = self.gpib.read() + print ('Wavelength Sent: %s' % info) + + #self.gpib.write('SOURCE0:CHAN1:POW:STATE 1') + self.outputON() + + + def sweepWavelengthsContinuous (self, start, end, power): + ''' + Executes a continuous sweep, not for use with triggered PowerMeters + + :param start: Specified wavelength between 1260-1360 nm, or 1500-1630 nm + :type start: Float + :param end: Specified wavelength between 1260-1360 nm, or 1500-1630 nm + :type end: Float + :param power: Specified power for sweep + :type power: Float + :Note: motor_speed is calculated from end - start to be < 2 seconds + :to avoid a timeout from PyVisa before laser scan completes + ''' + + self.outputOFF() + + # time to wait before checking OPC = operation complete bit + sleepTime = 0.01 + + # Calcualte motor speed to key scan time < 2 s + wavelength_span = (end - start) + max_scan_time = 2.0 #s + #reverse ordered to make search easy + motor_speed_list = [100,67,50,40,33,29,25,22,20,18,17,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1] + for x in motor_speed_list: + scan_time = wavelength_span /x + if scan_time < max_scan_time: + new_scan_time = scan_time + print ' new_scan_time',new_scan_time + print 'speed = ', + motor_speed = wavelength_span / new_scan_time + print 'scan motor_speed = ', motor_speed + print 'scan_time = ', new_scan_time + + if (start < self.min_wavelength + or start > self.max_wavelength + or end < self.min_wavelength + or end > self.max_wavelength + or end <= start): + print ('Specified Wavelengths Out of Range') + + else: + while not self.checkOPC(): + time.sleep(sleepTime) + else: + self.setpower(power) + + while not self.checkOPC(): + time.sleep(sleepTime) + else: + self.setwavelength(start) + + while not self.checkOPC(): + time.sleep(sleepTime) + else: + self.gpib.write('MOTOR_SPEED ' +str(motor_speed)) + + while not self.checkOPC(): + time.sleep(sleepTime) + else: + self.gpib.write('ACTCTRLON') + + while not self.checkOPC(): + time.sleep(sleepTime) + else: + self.setwavelength(end) + print 'setwavelength(end)' + + # maximum motor speed to return + while not self.checkOPC(): + print 'INSIDE not self.checkOPC' + time.sleep(sleepTime) + else: + self.gpib.write('MOTOR_SPEED = ' + '100') + self.gpib.write('MOTOR_SPEED?') + print 'MOTOR_SPEED = ', self.gpib.read() + + while not self.checkOPC(): + time.sleep(sleepTime) + else: + self.gpib.write('ACTCTRLOFF') + + def checkOPC(self): + # check OPC bit of STB status bit. Mask out other bits with & + return (int(self.gpib.write ('*STB?')[0] & 1)) + + def outputON(self): + ''' + Turns output of laser source ON + ''' + self.gpib.write('ENABLE') + + def outputOFF(self): + ''' + Turns output of laser source OFF + + ''' + self.gpib.write('DISABLE') + + def getwavelength(self): + ''' + Queries wavelength of the laser + + :returns: Float + ''' + self.gpib.write('L?') + return float(self.gpib.read()[2:]) + + def setpower(self, power = 0.0 ): + ''' + Sets power in dbm + + :param power: Specified power to set the laser to in dbm + :type power: Float + ''' + power = float(power) + if (power < -6.99) or (power > 13.4): + print 'Power setting out of -6.99 to +13.4 dBm range' + else: + self.gpib.write('P = ' + str(power)) + + def getpower(self): + ''' + Gets output power in dbm + + :returns: Float + ''' + self.gpib.write('P?') + self.power = self.gpib.read() + print 'power read as: ', type(self.power), self.power + if self.power[0:2] == 'P=': + print 'Laser ENABLED' + self.power = float(self.power[2:]) + else: + print 'Laser DISABLED' + self.power = 'DISABLED' + + return self.power + + def close(self): + ''' + Release resources + ''' + self.outputOFF() + self.gpib.close() + + +#if __name__ == "__main__": + +''' +Copyright (C) 2017 Robert Polster +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' diff --git a/ProberControl/prober/instruments/EXFOT100_Laser.py b/ProberControl/prober/instruments/EXFOT100_Laser.py new file mode 100644 index 0000000..17f7f8e --- /dev/null +++ b/ProberControl/prober/instruments/EXFOT100_Laser.py @@ -0,0 +1,225 @@ +import time +import visa + +class EXFOT100_Laser(object): + ''' + This class models the EXFO T100S-HP laser. There is a + 1260-1360 nm and a 1500-1630 nm version. + + .. note:: When using any laser command, remember to send shut-off-laser command at the end of each sweep command set. + For Trigger Sweep, send shut-off-laser command after sweep ends (sweep end condition noted in TriggerSweepSetup function) + Note that these lasers do not have trigger inputs or outputs. + ''' + + def __init__(self, res_manager, address='GPIB0::3::INSTR'): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: string + ''' + + self.active = False + + self.gpib = res_manager.open_resource(address) + + self.gpib.write ('*IDN?') + info = self.gpib.read() + print ('Connection Successful: %s' % info) + + #Ensure Output is OFF + self.gpib.write ('DISABLE') + + #Default Laser Power Unit to dBm + self.gpib.write ('DBM') + + #Default Spectral Unit Selection + self.gpib.write('NM') + + #Get min and max wavelengths + #self.get_min_wavelength() + #self.get_max_wavelength() + + def whoAmI(self): + ''':returns: reference to device''' + return 'Laser' + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + print 'state = ', self.active + + def chan_enable(self, channel): + print 'Enable CH' + str(int(channel)) +':ENABLE' + self.gpib.write('CH' + str(int(channel)) +':ENABLE') + + def chan_disable(self, channel): + print 'Disable CH' + str(int(channel)) +':DISABLE' + self.gpib.write('CH' + str(int(channel)) +':DISABLE') + + def All_chan_enable(self): + #Disable the laser output on allinstalled OCICS modules + self.gpib.write('ENABLE') + + def All_chan_disable(self): + #Enable the laser output on allinstalled OCICS modules + self.gpib.write('DISABLE') + + + def get_type(self, channel): # Updated 11/5/2019 Jim Davis + ''' + Queries the channel type + + :returns: String + ''' + if (channel == 1) or (channel == 2): + self.gpib.write ('CH' + str(int(channel)) +':TYPE?') + self.type = str(self.gpib.read()) + else: + print 'Channel not yet implemented in driver' + self.type = '' + + return self.type + + def setwavelength(self, channel, wavelength): + ''' + Loads a single wavelength and channel + Sets output wavelength + + :param waveLength: Specified wavelength + type channel: int + :type waveLength: Float + ''' + wavelength = float(wavelength) + if channel == 1: + if (wavelength < 1260.0) or (wavelength > 1360.0): + print 'Channel 1 Wavelength setting out of 1260.0 - 1360.0 nm range' + else: + self.chan_enable(channel) # Errors if channel not enabled + self.gpib.write('CH' + str(int(channel)) + ':L = ' + str(wavelength)) + elif channel == 2: + if (wavelength < 1520.0) or (wavelength > 1630.0): + print 'Channel 2 Wavelength setting out of 1520.0 - 1630.0 range' + else: + self.chan_enable(channel) # Errors if channel not enabled + self.gpib.write('CH' + str(int(channel)) + ':L = ' + str(wavelength)) + else: + print 'Channel not yet implemented in driver' + + def checkOPC(self): + # check OPC bit of STB status bit. Mask out other bits with & + return (int(self.gpib.write ('*STB?')[0] & 1)) + + def getwavelength(self, channel): + ''' + Queries wavelength of the laser + Checks wavelength by channel + + :returns: float + ''' + if (channel == 1) or (channel == 2): + self.gpib.write('CH' + str(int(channel)) + ':L?') + return float(self.gpib.read()[6:]) + else: + print 'Channel not yet implemented in driver' + return '' + + def setpower(self, channel, power = 0.0 ): + ''' + Sets power in dbm for channels implement in driver + Checks range for each laser + + :param power: Specified power to set the laser to in dbm + :type power: Float + ''' + power = float(power) + if channel == 1: + if (power < -6.9) or (power > 11.6): + print 'Channel 1 Power setting out of -6.9 to +11.6 dBm range' + else: + self.gpib.write('CH' + str(int(channel)) +':ENABLE') + self.gpib.write('CH' + str(int(channel)) + ':P = ' + str(power)) + elif channel == 2: + if (power < -6.9) or (power > 7.8): + print 'Channel 2 Power setting out of -6.9 to +7.8 dBm range' + else: + self.gpib.write('CH' + str(int(channel)) +':ENABLE') + self.gpib.write('CH' + str(int(channel)) + ':P = ' + str(power)) + else: + print 'Channel not yet implemented in driver' + + + + def getpower(self, channel): + ''' + Gets output power in dbm + + :returns: Float + ''' + + if (channel == 1) or (channel == 2): + self.gpib.write('CH' + str(int(channel)) + ':P?') + self.power = self.gpib.read() + return self.power + else: + print 'Channel not yet implemented in driver' + self.power = '' + return self.power + + def Coherence_ctrl (self, channel, ctrl): + ''' + Sets coherence control on for ctrl = 1, off for ctrl = 0 + Returns 0 for Coherence control OFF, 1 for Coherence control ON + ''' + + if (channel == 1) or (channel == 2): + if (ctrl == 0): + + self.gpib.write('CH' + str(int(channel)) + ':CTRL OFF') + self.gpib.write('CH' + str(int(channel)) + ':CTRL?') + Coherence = self.gpib.read() + print 'CH', str(int(channel)), ':CTRL OFF' + return Coherence[4] + #print self.gpib.read()[4] + elif (ctrl == 1): + self.gpib.write('CH' + str(int(channel)) + ':CTRL ON') + self.gpib.write('CH' + str(int(channel)) + ':CTRL?') + Coherence = self.gpib.read() + print 'CH', str(int(channel)), ':CTRL ON' + return Coherence[4] + else: + print 'Error in ctrl: ON: ctrl=1, OFF: ctrl=0' + + else: + print 'Channel not yet implemented in driver' + return '' + + def close(self): + ''' + Release resources + ''' + self.All_chan_disable() + self.gpib.close() + +#if __name__ == "__main__": + +''' +Copyright (C) 2017 Robert Polster +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' diff --git a/ProberControl/prober/instruments/GenPhotPCD104.py b/ProberControl/prober/instruments/GenPhotPCD104.py new file mode 100644 index 0000000..7762f9d --- /dev/null +++ b/ProberControl/prober/instruments/GenPhotPCD104.py @@ -0,0 +1,79 @@ +import time +import visa + +class GenPhotPCD104(object): + ''' + This class models the General Photonics Polarization Scrambler + + .. note:: When using any laser command, remember to send shut-off-laser command at the end of each sweep command set. + For Trigger Sweep, send shut-off-laser command after sweep ends (sweep end condition noted in TriggerSweepSetup function) + ''' + + def __init__(self, res_manager, address='GPIB0::5::INSTR'): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: string + ''' + self.active = False + self.gpib = res_manager.open_resource(address) + self.gpib.write ('*IDN?') + info = self.gpib.read() + print ('Connection Successful: %s' % info) + + def whoAmI(self): + ''':returns: reference to device''' + return 'PolScramb' + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + + def getWavelength(self): + '''Get current Wavelength''' + self.gpib.write('*WAV?') + info = self.gpib.read() + return info + + def enable(self): + '''Enable Scrambling''' + self.gpib.write('*ENA#') + + def disable(self): + '''Disable Scrambling''' + self.gpib.write('*DIS#') + + def setWavelength(self, wavelength): + ''' + Set Wavelength of Operation + ''' + if wavelength != 980 and wavelength != 1060 and wavelength != 1310 and wavelength != 1480 and wavelength != 1550 and wavelength != 1600: + return "Invalid wavelength. Please try again." + else: + self.gpib.write('*WAV ' + str(int(wavelength))+ '#') + return "Success" + + +#if __name__ == "__main__": + +''' +Copyright (C) 2017 Robert Polster +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' diff --git a/ProberControl/prober/instruments/Keysight8164B_Laser - Copy.py b/ProberControl/prober/instruments/Keysight8164B_Laser - Copy.py new file mode 100644 index 0000000..b4bb9aa --- /dev/null +++ b/ProberControl/prober/instruments/Keysight8164B_Laser - Copy.py @@ -0,0 +1,412 @@ +import time +import visa + +class Keysight8164B_Laser(object): + ''' + This class models the a Keysight 8164A/B Lightwave Multimeter. It controls + the Keysight 81608A Laser. + + .. note:: When using any laser command, remember to send shut-off-laser command at the end of each sweep command set. + For Trigger Sweep, send shut-off-laser command after sweep ends (sweep end condition noted in TriggerSweepSetup function) + ''' + + def __init__(self, res_manager, address='GPIB0::1::INSTR'): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: string + ''' + + self.active = False + + self.gpib = res_manager.open_resource(address) + self.gpib.write('LOCK 0, 1234') + + self.gpib.write ('*IDN?') + info = self.gpib.read() + print ('Connection Successful: %s' % info) + + self.gpib.write ('LOCK?') + info = self.gpib.read() + print('Locked: %s' %info) + + #Ensure Output is OFF + self.gpib.write ('SOURCE0:CHAN1:POW:STATE 0') + + #Default Laser Power Unit to dBm + self.gpib.write ('SOURCE0:CHAN1:POW:UNIT 0') + + #Enables Display + self.gpib.write ('DISP:ENAB 1') + + #Check Output Status + time.sleep(0.55) + self.gpib.write ('SOURCE0:CHAN1:POW:STATE?') + info = self.gpib.read() + print('Output: %s' %info) + + + + def whoAmI(self): + ''':returns: reference to device''' + return 'Laser' + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + print 'state = ', self.active + + def get_max_wavelength(self): # Updated 9/20/2019 Jim Davis + ''' + Queries the maximum allowed wavelength + + :returns: Integer + ''' + self.gpib.write ('SOURCE0:CHAN1:WAV? MAX') + max_wavelength = self.gpib.read() + self.max_wavelength = int(float(max_wavelength) * 1E9) - 1 # correct for 1 nm error + #print 'Wavelength Max: ', self.max_wavelength, 'nm' + return self.max_wavelength + + def get_min_wavelength(self): # Updated 9/20/2019 Jim Davis + ''' + Queries the minimum allowed wavelength + + :returns: Integer + ''' + self.gpib.write ('SOURCE0:CHAN1:WAV? MIN') + min_wavelength = self.gpib.read() + self.min_wavelength = int(float(min_wavelength) * 1E9) + 1 # correct for 1 nm error + #print 'Wavelength Min: ', self.min_wavelength, 'nm' + return self.min_wavelength + + def setwavelength(self, wavelength): + ''' + Loads a single wavelength and sets output high + + :param waveLength: Specified wavelength + :type waveLength: Integer + ''' + self.outputOFF() + + if wavelength < self.min_wavelength or wavelength > self.max_wavelength: + print ('Specified Wavelength Out of Range: ' +str(wavelength)) + else : + # Execute setting of wavelength + self.gpib.write('SOURCE0:CHAN1:WAV ' + str(wavelength)+ "nm") + print(str(wavelength)) + time.sleep(0.55) + self.gpib.write('SOURCE0:CHAN1:WAV?') + info = self.gpib.read() + print ('Wavelength Sent: %s' % info) + + #self.gpib.write('SOURCE0:CHAN1:POW:STATE 1') + self.outputON() + + #High for test period + while self.checkStatusSingle() == False: + time.sleep(0.2) + # Print if successful + print('Single Wavelength Complete') + + + def sweepWavelengthsContinuousTriggerOut (self, start, end, step, speed = 1): + ''' + Executes a sweep, for use with triggered PowerMeters + + :param start: Specified wavelength between 1520-1580 + :type start: Integer + :param end: Specified wavelength between 1520-1580 + :type end: Integer + :param speed: Specified nm/s + :type time: Float + ''' + + self.outputOFF() + + if start < self.min_wavelength or start > self.max_wavelength or end < self.min_wavelength or end > self.max_wavelength: + print ('Specified Wavelengths Out of Range') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE CONT') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SPE ' + str(speed) + 'NM/S') + + + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:LLOG 0') #Start Logging + self.gpib.write('TRIG0:CHAN1:OUTP STF') #Trigger on + self.gpib.write('SOURCE0:CHAN1:AM:STAT OFF') #Disabled Amplitude Modulation + + info = int(self.numberOfTriggers()) + print ('Number of Triggers %s' % info) + + self.outputON() + + def sweepWavelengthsContinuous (self, start, end, step, speed = 1): + ''' + Executes a sweep with respect to a specified time + + :param start: Specified wavelength between 1520-1580 + :type start: Integer + :param end: Specified wavelength between 1520-1580 + :type end: Integer + :param speed: Specified nm/s + :type time: Float + ''' + + self.outputOFF() + self.stopSweep() + + if start < self.min_wavelength or start > self.max_wavelength or end < self.min_wavelength or end > self.max_wavelength: + print ('Specified Wavelengths Out of Range') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE CONT') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SPE ' + str(speed) + 'NM/S') + self.outputON() + + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR?') + start_1 = self.gpib.read() + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP?') + end_1 = self.gpib.read() + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SPE?') + spe_1 = self.gpib.read() + + print ('Sweep Parameters: %s %s %s' % (start_1, end_1, spe_1)) + + self.startSweep() #Single Sweep + + while self.checkSweepStatus() == False: + pass + print ('Sweep Wavelength Continuous Complete') + self.outputOFF() + + def sweepWavelengthsStep (self, start, end, step, dwell): + ''' + Have to keep track of Triggers in main command, use Stop Sweep Command to end sweep. + Extra triggers do not make the laser sweep outside of specified end wavelength. + Remeber to shut off laser after sweep ends + + :param start: Specified wavelength between 1520-1580 + :type start: Integer + :param end: Specified wavelength between 1520-1580 + :type end: Integer + :param time: Specified time + :type time: Float + ''' + + self.outputOFF() + self.stopSweep() + + if ( + float(start) < self.min_wavelength or + float(start) > self.max_wavelength or + float(end) < self.min_wavelength or + float(end) > self.max_wavelength or + float(step) < 0.001 + ): + print ('Specified Wavelengths Out of Range, or Step Too Low') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE:STEP') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:DWEL ' + str(dwell) + 'MS') + self.outputON(); + self.startSweep(); #Single Sweep + print ('Stepped Sweep Started') + while self.checkSweepStatus() == False: + pass + print ('Sweep Wavelengths Step Complete') + self.outputOFF() + + def numberOfTriggers (self): + '''Number of Triggers for Sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:EXP?') + info = self.gpib.read() + print info + return info + + def trigger(self): + '''Software Triggers laser''' + + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SOFT') + + def step(self): + '''Manual Step laser''' + + time.sleep(0.4) + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP:NEXT') + time.sleep(0.05) + + def checkSweepStatus(self): + ''' + Checks the status of the laser. Handles timeout exception + + :returns: Boolean + ''' + + try: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE?') + status = int (self.gpib.read()) + #self.gpib.write('SOURCE0:CHAN1:WAV:SWE:FLAG?') + #print int(self.gpib.read()) + print('.'), + if status == 0: + return True + else: + return False + except Exception: + time.sleep(0.2) + return self.checkSweepStatus() + + + def checkStatusSingle(self): + ''' + Checks the status of the laser, just once. Handles timeout exception + + :returns: Boolean + ''' + + self.gpib.write('*OPC?') + status = int(self.gpib.read()) + if status > 0: + return True + else: + return False + + def sweepWavelengthsManual(self, start, end, step): + ''' + Use if want to manually step by a particular size, in conjuction with Send Single Wavelength + + :param step: specified step increment, but be greater than or equal to 0.001 + :type step: Float + ''' + self.outputOFF() + self.stopSweep() + + if ( + float(start) < self.min_wavelength or + float(start) > self.max_wavelength or + float(end) < self.min_wavelength or + float(end) > self.max_wavelength or + float(step) < 0.001 + ): + print ('Specified Wavelengths Out of Range, or Step Too Low') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE MAN') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.outputON(); + self.startSweep(); #Single Sweep + print ('Manual Setup Complete') + + + def startSweep(self): + '''Start sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE STAR') + + def stopSweep(self): + '''Stop sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE STOP') + + def pauseSweep(self): + '''Suspend sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE PAUS') + + def resumeSweep(self): + ''' + Use after pause to resume, still have to call trigger() for next data point if using with trigger sweep + ''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE CONT') + + def outputON(self): + ''' + Turns output of laser source ON + ''' + self.gpib.write('SOURCE0:CHAN1:POW:STATE 1') + + def outputOFF(self): + ''' + Turns output of laser source OFF + + .. note:: Output occasionally doesn't turn off unless turned ON beforehand + ''' + self.gpib.write('SOURCE0:CHAN1:POW:STATE 0') + + def getwavelength(self): # 9/23 Change to return nm to match the rest of the functions using nm. Jim Davis + ''' + Queries wavelength of the laser + + :returns: Float + ''' + self.gpib.write('SOURCE0:CHAN1:WAV?') + return int(float(self.gpib.read()) * 1.0E9) + + def setpower(self,power = 0 ): + ''' + Sets power in dbm + + :param power: Specified power to set the laser to in dbm + :type power: Integer + ''' + self.gpib.write('SOURCE0:CHAN1:POW ' + str(power)) + + def getpower(self): + ''' + Gets output power in dbm + + :returns: Float + ''' + self.gpib.write('SOURCE0:CHAN1:POW?') + return float(self.gpib.read()) + + def reset(self): + ''' + Release resources + ''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:LLOG 0') #Start Logging + self.gpib.write('TRIG0:CHAN1:OUTP DIS') #Trigger on + self.gpib.write('SOURCE0:CHAN1:AM:STAT OFF') #Disabled Amplitude Modulation + + return 'Laser Reset' + + def close(self): + ''' + Release resources + ''' + self.outputOFF() + self.gpib.close() + + +#if __name__ == "__main__": + +''' +Copyright (C) 2017 Robert Polster +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' diff --git a/ProberControl/prober/instruments/Keysight8164B_Laser.py b/ProberControl/prober/instruments/Keysight8164B_Laser.py new file mode 100644 index 0000000..b4bb9aa --- /dev/null +++ b/ProberControl/prober/instruments/Keysight8164B_Laser.py @@ -0,0 +1,412 @@ +import time +import visa + +class Keysight8164B_Laser(object): + ''' + This class models the a Keysight 8164A/B Lightwave Multimeter. It controls + the Keysight 81608A Laser. + + .. note:: When using any laser command, remember to send shut-off-laser command at the end of each sweep command set. + For Trigger Sweep, send shut-off-laser command after sweep ends (sweep end condition noted in TriggerSweepSetup function) + ''' + + def __init__(self, res_manager, address='GPIB0::1::INSTR'): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: string + ''' + + self.active = False + + self.gpib = res_manager.open_resource(address) + self.gpib.write('LOCK 0, 1234') + + self.gpib.write ('*IDN?') + info = self.gpib.read() + print ('Connection Successful: %s' % info) + + self.gpib.write ('LOCK?') + info = self.gpib.read() + print('Locked: %s' %info) + + #Ensure Output is OFF + self.gpib.write ('SOURCE0:CHAN1:POW:STATE 0') + + #Default Laser Power Unit to dBm + self.gpib.write ('SOURCE0:CHAN1:POW:UNIT 0') + + #Enables Display + self.gpib.write ('DISP:ENAB 1') + + #Check Output Status + time.sleep(0.55) + self.gpib.write ('SOURCE0:CHAN1:POW:STATE?') + info = self.gpib.read() + print('Output: %s' %info) + + + + def whoAmI(self): + ''':returns: reference to device''' + return 'Laser' + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + print 'state = ', self.active + + def get_max_wavelength(self): # Updated 9/20/2019 Jim Davis + ''' + Queries the maximum allowed wavelength + + :returns: Integer + ''' + self.gpib.write ('SOURCE0:CHAN1:WAV? MAX') + max_wavelength = self.gpib.read() + self.max_wavelength = int(float(max_wavelength) * 1E9) - 1 # correct for 1 nm error + #print 'Wavelength Max: ', self.max_wavelength, 'nm' + return self.max_wavelength + + def get_min_wavelength(self): # Updated 9/20/2019 Jim Davis + ''' + Queries the minimum allowed wavelength + + :returns: Integer + ''' + self.gpib.write ('SOURCE0:CHAN1:WAV? MIN') + min_wavelength = self.gpib.read() + self.min_wavelength = int(float(min_wavelength) * 1E9) + 1 # correct for 1 nm error + #print 'Wavelength Min: ', self.min_wavelength, 'nm' + return self.min_wavelength + + def setwavelength(self, wavelength): + ''' + Loads a single wavelength and sets output high + + :param waveLength: Specified wavelength + :type waveLength: Integer + ''' + self.outputOFF() + + if wavelength < self.min_wavelength or wavelength > self.max_wavelength: + print ('Specified Wavelength Out of Range: ' +str(wavelength)) + else : + # Execute setting of wavelength + self.gpib.write('SOURCE0:CHAN1:WAV ' + str(wavelength)+ "nm") + print(str(wavelength)) + time.sleep(0.55) + self.gpib.write('SOURCE0:CHAN1:WAV?') + info = self.gpib.read() + print ('Wavelength Sent: %s' % info) + + #self.gpib.write('SOURCE0:CHAN1:POW:STATE 1') + self.outputON() + + #High for test period + while self.checkStatusSingle() == False: + time.sleep(0.2) + # Print if successful + print('Single Wavelength Complete') + + + def sweepWavelengthsContinuousTriggerOut (self, start, end, step, speed = 1): + ''' + Executes a sweep, for use with triggered PowerMeters + + :param start: Specified wavelength between 1520-1580 + :type start: Integer + :param end: Specified wavelength between 1520-1580 + :type end: Integer + :param speed: Specified nm/s + :type time: Float + ''' + + self.outputOFF() + + if start < self.min_wavelength or start > self.max_wavelength or end < self.min_wavelength or end > self.max_wavelength: + print ('Specified Wavelengths Out of Range') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE CONT') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SPE ' + str(speed) + 'NM/S') + + + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:LLOG 0') #Start Logging + self.gpib.write('TRIG0:CHAN1:OUTP STF') #Trigger on + self.gpib.write('SOURCE0:CHAN1:AM:STAT OFF') #Disabled Amplitude Modulation + + info = int(self.numberOfTriggers()) + print ('Number of Triggers %s' % info) + + self.outputON() + + def sweepWavelengthsContinuous (self, start, end, step, speed = 1): + ''' + Executes a sweep with respect to a specified time + + :param start: Specified wavelength between 1520-1580 + :type start: Integer + :param end: Specified wavelength between 1520-1580 + :type end: Integer + :param speed: Specified nm/s + :type time: Float + ''' + + self.outputOFF() + self.stopSweep() + + if start < self.min_wavelength or start > self.max_wavelength or end < self.min_wavelength or end > self.max_wavelength: + print ('Specified Wavelengths Out of Range') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE CONT') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SPE ' + str(speed) + 'NM/S') + self.outputON() + + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR?') + start_1 = self.gpib.read() + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP?') + end_1 = self.gpib.read() + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SPE?') + spe_1 = self.gpib.read() + + print ('Sweep Parameters: %s %s %s' % (start_1, end_1, spe_1)) + + self.startSweep() #Single Sweep + + while self.checkSweepStatus() == False: + pass + print ('Sweep Wavelength Continuous Complete') + self.outputOFF() + + def sweepWavelengthsStep (self, start, end, step, dwell): + ''' + Have to keep track of Triggers in main command, use Stop Sweep Command to end sweep. + Extra triggers do not make the laser sweep outside of specified end wavelength. + Remeber to shut off laser after sweep ends + + :param start: Specified wavelength between 1520-1580 + :type start: Integer + :param end: Specified wavelength between 1520-1580 + :type end: Integer + :param time: Specified time + :type time: Float + ''' + + self.outputOFF() + self.stopSweep() + + if ( + float(start) < self.min_wavelength or + float(start) > self.max_wavelength or + float(end) < self.min_wavelength or + float(end) > self.max_wavelength or + float(step) < 0.001 + ): + print ('Specified Wavelengths Out of Range, or Step Too Low') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE:STEP') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:DWEL ' + str(dwell) + 'MS') + self.outputON(); + self.startSweep(); #Single Sweep + print ('Stepped Sweep Started') + while self.checkSweepStatus() == False: + pass + print ('Sweep Wavelengths Step Complete') + self.outputOFF() + + def numberOfTriggers (self): + '''Number of Triggers for Sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:EXP?') + info = self.gpib.read() + print info + return info + + def trigger(self): + '''Software Triggers laser''' + + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:SOFT') + + def step(self): + '''Manual Step laser''' + + time.sleep(0.4) + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP:NEXT') + time.sleep(0.05) + + def checkSweepStatus(self): + ''' + Checks the status of the laser. Handles timeout exception + + :returns: Boolean + ''' + + try: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE?') + status = int (self.gpib.read()) + #self.gpib.write('SOURCE0:CHAN1:WAV:SWE:FLAG?') + #print int(self.gpib.read()) + print('.'), + if status == 0: + return True + else: + return False + except Exception: + time.sleep(0.2) + return self.checkSweepStatus() + + + def checkStatusSingle(self): + ''' + Checks the status of the laser, just once. Handles timeout exception + + :returns: Boolean + ''' + + self.gpib.write('*OPC?') + status = int(self.gpib.read()) + if status > 0: + return True + else: + return False + + def sweepWavelengthsManual(self, start, end, step): + ''' + Use if want to manually step by a particular size, in conjuction with Send Single Wavelength + + :param step: specified step increment, but be greater than or equal to 0.001 + :type step: Float + ''' + self.outputOFF() + self.stopSweep() + + if ( + float(start) < self.min_wavelength or + float(start) > self.max_wavelength or + float(end) < self.min_wavelength or + float(end) > self.max_wavelength or + float(step) < 0.001 + ): + print ('Specified Wavelengths Out of Range, or Step Too Low') + + else: + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:MODE MAN') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STAR ' + str(start) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STOP ' + str(end) + 'NM') + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:STEP ' + str(step) + 'NM') + self.outputON(); + self.startSweep(); #Single Sweep + print ('Manual Setup Complete') + + + def startSweep(self): + '''Start sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE STAR') + + def stopSweep(self): + '''Stop sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE STOP') + + def pauseSweep(self): + '''Suspend sweep''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE PAUS') + + def resumeSweep(self): + ''' + Use after pause to resume, still have to call trigger() for next data point if using with trigger sweep + ''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE CONT') + + def outputON(self): + ''' + Turns output of laser source ON + ''' + self.gpib.write('SOURCE0:CHAN1:POW:STATE 1') + + def outputOFF(self): + ''' + Turns output of laser source OFF + + .. note:: Output occasionally doesn't turn off unless turned ON beforehand + ''' + self.gpib.write('SOURCE0:CHAN1:POW:STATE 0') + + def getwavelength(self): # 9/23 Change to return nm to match the rest of the functions using nm. Jim Davis + ''' + Queries wavelength of the laser + + :returns: Float + ''' + self.gpib.write('SOURCE0:CHAN1:WAV?') + return int(float(self.gpib.read()) * 1.0E9) + + def setpower(self,power = 0 ): + ''' + Sets power in dbm + + :param power: Specified power to set the laser to in dbm + :type power: Integer + ''' + self.gpib.write('SOURCE0:CHAN1:POW ' + str(power)) + + def getpower(self): + ''' + Gets output power in dbm + + :returns: Float + ''' + self.gpib.write('SOURCE0:CHAN1:POW?') + return float(self.gpib.read()) + + def reset(self): + ''' + Release resources + ''' + self.gpib.write('SOURCE0:CHAN1:WAV:SWE:LLOG 0') #Start Logging + self.gpib.write('TRIG0:CHAN1:OUTP DIS') #Trigger on + self.gpib.write('SOURCE0:CHAN1:AM:STAT OFF') #Disabled Amplitude Modulation + + return 'Laser Reset' + + def close(self): + ''' + Release resources + ''' + self.outputOFF() + self.gpib.close() + + +#if __name__ == "__main__": + +''' +Copyright (C) 2017 Robert Polster +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' diff --git a/ProberControl/prober/instruments/Keysight8164B_PowerMeter - Copy.py b/ProberControl/prober/instruments/Keysight8164B_PowerMeter - Copy.py new file mode 100644 index 0000000..9a9957e --- /dev/null +++ b/ProberControl/prober/instruments/Keysight8164B_PowerMeter - Copy.py @@ -0,0 +1,165 @@ +#import visa +#import time +#import sys +import struct + +class Keysight8164B_PowerMeter(object): + ''' + This class models a Keysight 8164A/B Lightwave Multimeter. + It has been tested with the 81630B (High power range of +28dBm)and + 81636B (Fast for sweeps) + power sensors. + ''' + + CURRENT_CHANNEL = 1 + + def __init__(self,res_manager,address='GPIB0::1::INSTR', channel=1): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: String + ''' + self.active = False + self.gpib = res_manager.open_resource(address) + if '.' in str(channel): + self.__channel = int(channel.split('.')[0]) + self.__port = int(channel.split('.')[1]) + else: + self.__channel = channel + self.__port = 1 + + # Set Power Unit to dbm + self.gpib.write('sens' + self.__channel + ':pow:unit 0') + + # Zeros the electrical offsets for a power meter or return loss module [9/20/2019 Jim Davis] + self.gpib.write('sens' + self.__channel + ':corr:coll:zero') + + def _checkChannel(self): + + if CURRENT_CHANNEL != self.__channel: + _setChannel(self.__channel) + + def _setChannel(self, newChannel): + ''' + The purpose of this method is to change channels + The syntax of usage will depend on the particular device. + ''' + CURRENT_CHANNEL = newChannel + self.__channel = newChannel + + + def whoAmI(self): + + ''':returns: reference to device''' + print self + return 'PowerMeter' + + def get_power(self,wavelength=1550): + ''' return power meter reading after setting correct wavelength''' + print 'sens',str(int(self.__channel)),':chan',str(int(self.__port)),':pow:wav ',str(wavelength),'nm' + + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:wav '+str(wavelength)+'nm') + return float(self.gpib.query('read'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow?')) + + def get_feedback(self): + return self.get_power() + + + def close(self): + ''' + Release resources + ''' + self.gpib.close() + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + + def config_meter(self, range = 10): + if self.__port != 2: + range = int(range) + + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:unit 0') + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:unit?') + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:range:auto 0') #Auto ranging turned off + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:rang '+str(range)+'DBM') + self.gpib.write('trig'+str(int(self.__channel))+':inp:rearm on') + + def prep_measure_on_trigger(self, samples = 64): + if self.__port != 2: + self.gpib.write('*CLS') + samples = int(samples) + + #self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat stab,stop') #switch stab with logg depending + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat logg,stop') #switch stab with logg depending + self.gpib.write('trig'+str(int(self.__channel))+':chan'+str(int(self.__port))+':inp sme') #Set up trigger + print self.gpib.query('trig'+str(int(self.__channel))+':inp?') + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:par:logg '+str(samples)+',100us') + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:par:logg?') + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat logg,start') + + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat?') + print self.gpib.query('syst:err?') + + + def get_result_from_log(self,samples=64): + + if self.__port != 2: + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat?') + + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:res?') + data = self.gpib.read_raw() + print self.gpib.query('syst:err?') + + + samples = int(samples) + + #print data + + NofDigits = int(data[1]) + + HexData = data[2+NofDigits:2+NofDigits+samples*4] + + FloData = [] + + for x in range(0, samples*4-1,4): + dat = HexData[x:x+4] + val = struct.unpack('. +''' diff --git a/ProberControl/prober/instruments/Keysight8164B_PowerMeter.py b/ProberControl/prober/instruments/Keysight8164B_PowerMeter.py new file mode 100644 index 0000000..9a9957e --- /dev/null +++ b/ProberControl/prober/instruments/Keysight8164B_PowerMeter.py @@ -0,0 +1,165 @@ +#import visa +#import time +#import sys +import struct + +class Keysight8164B_PowerMeter(object): + ''' + This class models a Keysight 8164A/B Lightwave Multimeter. + It has been tested with the 81630B (High power range of +28dBm)and + 81636B (Fast for sweeps) + power sensors. + ''' + + CURRENT_CHANNEL = 1 + + def __init__(self,res_manager,address='GPIB0::1::INSTR', channel=1): + ''' + Constructor method + + :param res_manager: PyVisa resource manager + :type res_manager: PyVisa resourceManager object + :param address: SCPI address of instrument + :type address: String + ''' + self.active = False + self.gpib = res_manager.open_resource(address) + if '.' in str(channel): + self.__channel = int(channel.split('.')[0]) + self.__port = int(channel.split('.')[1]) + else: + self.__channel = channel + self.__port = 1 + + # Set Power Unit to dbm + self.gpib.write('sens' + self.__channel + ':pow:unit 0') + + # Zeros the electrical offsets for a power meter or return loss module [9/20/2019 Jim Davis] + self.gpib.write('sens' + self.__channel + ':corr:coll:zero') + + def _checkChannel(self): + + if CURRENT_CHANNEL != self.__channel: + _setChannel(self.__channel) + + def _setChannel(self, newChannel): + ''' + The purpose of this method is to change channels + The syntax of usage will depend on the particular device. + ''' + CURRENT_CHANNEL = newChannel + self.__channel = newChannel + + + def whoAmI(self): + + ''':returns: reference to device''' + print self + return 'PowerMeter' + + def get_power(self,wavelength=1550): + ''' return power meter reading after setting correct wavelength''' + print 'sens',str(int(self.__channel)),':chan',str(int(self.__port)),':pow:wav ',str(wavelength),'nm' + + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:wav '+str(wavelength)+'nm') + return float(self.gpib.query('read'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow?')) + + def get_feedback(self): + return self.get_power() + + + def close(self): + ''' + Release resources + ''' + self.gpib.close() + + def change_state(self): + + if self.active == True: + self.active = False + else: + self.active = True + + def config_meter(self, range = 10): + if self.__port != 2: + range = int(range) + + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:unit 0') + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:unit?') + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:range:auto 0') #Auto ranging turned off + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':pow:rang '+str(range)+'DBM') + self.gpib.write('trig'+str(int(self.__channel))+':inp:rearm on') + + def prep_measure_on_trigger(self, samples = 64): + if self.__port != 2: + self.gpib.write('*CLS') + samples = int(samples) + + #self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat stab,stop') #switch stab with logg depending + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat logg,stop') #switch stab with logg depending + self.gpib.write('trig'+str(int(self.__channel))+':chan'+str(int(self.__port))+':inp sme') #Set up trigger + print self.gpib.query('trig'+str(int(self.__channel))+':inp?') + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:par:logg '+str(samples)+',100us') + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:par:logg?') + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat logg,start') + + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat?') + print self.gpib.query('syst:err?') + + + def get_result_from_log(self,samples=64): + + if self.__port != 2: + print self.gpib.query('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:stat?') + + self.gpib.write('sens'+str(int(self.__channel))+':chan'+str(int(self.__port))+':func:res?') + data = self.gpib.read_raw() + print self.gpib.query('syst:err?') + + + samples = int(samples) + + #print data + + NofDigits = int(data[1]) + + HexData = data[2+NofDigits:2+NofDigits+samples*4] + + FloData = [] + + for x in range(0, samples*4-1,4): + dat = HexData[x:x+4] + val = struct.unpack('. +''' diff --git a/ProberControl/prober/instruments/PIXe_4140.py b/ProberControl/prober/instruments/PIXe_4140.py new file mode 100644 index 0000000..645eafc --- /dev/null +++ b/ProberControl/prober/instruments/PIXe_4140.py @@ -0,0 +1,181 @@ +""" Driver for NI PIXe-4322 6 channel SMU + + Allows for user interfacing directly, as well as ProberControl API + + see https://probercontrol.github.io/ProberControl/source/howto/addNewInstrument.html for + more info +""" +import sys +import argparse + +import nidcpower + +class CustomFormatter(argparse.RawDescriptionHelpFormatter, + argparse.ArgumentDefaultsHelpFormatter): + pass + +def parse_args(args=sys.argv[1:]): + """Parse arguments.""" + parser = argparse.ArgumentParser( + description=sys.modules[__name__].__doc__, + formatter_class=CustomFormatter) + + g = parser.add_argument_group("driver settings") + g.add_argument("address", metavar="gpib_address", + type=str, + help="The address of the device to be controlled") + g.add_argument("channel", metavar="device_channel", + type=str, + help="The channel to be controlled for the device") + a = parser.add_argument_group("driver actions") + a.add_argument("function", metavar="function_name", + type=str, + help="The driver function to be run") + a.add_argument("--value", metavar="function value", + type=str, + help="The value to be set for the function") + + return parser.parse_args(args) + +class PIXe_4140(object): + def __init__(self, rm, address='PXI1Slot2', channel='ao0', **kwargs): + self.address = address + self.channel = channel + self.last_set_voltage = 0 + self.last_set_current = 0 + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_enabled = False + session.initiate() + + def setvoltage(self,value): + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_function = nidcpower.OutputFunction.DC_VOLTAGE + session.output_enabled = True + session.voltage_level_autorange = True + session.voltage_level = float(value) + session.initiate() + + def setcurrent(self,value): + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_function = nidcpower.OutputFunction.DC_CURRENT + session.output_enabled = True + session.current_level_autorange = True + session.current_level = float(value) + session.initiate() + + def set_voltage_get_voltage(self, value, query_range): + query_range = int(query_range) + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_function = nidcpower.OutputFunction.DC_VOLTAGE + session.output_enabled = True + session.voltage_level_autorange = True + session.measure_record_length = query_range + session.measure_record_length_is_finite = True + session.measure_when = nidcpower.MeasureWhen.AUTOMATICALLY_AFTER_SOURCE_COMPLETE + session.voltage_level = float(value) + samples = [] + samples_acquired = 0 + with session.initiate(): + while samples_acquired < query_range: + measurements = session.fetch_multiple(count=session.fetch_backlog) + samples_acquired += len(measurements) + for i in range(len(measurements)): + samples.append(measurements[i].voltage) + + return samples + + + def set_current_get_voltage(self, value, query_range): + query_range = int(query_range) + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_function = nidcpower.OutputFunction.DC_CURRENT + session.output_enabled = True + session.current_level_autorange = True + session.measure_record_length = query_range + session.measure_record_length_is_finite = True + session.measure_when = nidcpower.MeasureWhen.AUTOMATICALLY_AFTER_SOURCE_COMPLETE + session.current_level = float(value) + samples = [] + samples_acquired = 0 + with session.initiate(): + while samples_acquired < query_range: + measurements = session.fetch_multiple(count=session.fetch_backlog) + samples_acquired += len(measurements) + for i in range(len(measurements)): + samples.append(measurements[i].voltage) + + return samples + + def set_voltage_get_current(self, value, query_range): + query_range = int(query_range) + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_function = nidcpower.OutputFunction.DC_VOLTAGE + session.output_enabled = True + session.voltage_level_autorange = True + session.measure_record_length = query_range + session.measure_record_length_is_finite = True + session.measure_when = nidcpower.MeasureWhen.AUTOMATICALLY_AFTER_SOURCE_COMPLETE + session.voltage_level = float(value) + samples = [] + samples_acquired = 0 + with session.initiate(): + while samples_acquired < query_range: + measurements = session.fetch_multiple(count=session.fetch_backlog) + samples_acquired += len(measurements) + for i in range(len(measurements)): + samples.append(measurements[i].current) + + return samples + + + def set_current_get_current(self, value, query_range): + query_range = int(query_range) + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_function = nidcpower.OutputFunction.DC_CURRENT + session.output_enabled = True + session.current_level_autorange = True + session.measure_record_length = query_range + session.measure_record_length_is_finite = True + session.measure_when = nidcpower.MeasureWhen.AUTOMATICALLY_AFTER_SOURCE_COMPLETE + session.current_level = float(value) + samples = [] + samples_acquired = 0 + with session.initiate(): + while samples_acquired < query_range: + measurements = session.fetch_multiple(count=session.fetch_backlog) + samples_acquired += len(measurements) + for i in range(len(measurements)): + samples.append(measurements[i].current) + + return samples + + def retreive_voltage(self): + return self.voltage + + def whoAmI(self): + return 'DCSource' + + def source_off(self): + '''Release resources''' + with nidcpower.Session(resource_name=self.address, channels=self.channel) as session: + session.output_enabled = False + session.initiate() + +def main(options, stdout): + device = PIXe_4140(options.address,options.channel) + if hasattr(PIXe_4140, options.function): + if isempty(options.value): + value = getattr(device, options.function) + else: + value = getattr(device, options.function)(options.value) + print('Function: %s' % options.function) + else: + print("Function does not exist.") + +if __name__ == '__main__': + def _script_io(): + from sys import argv, stdout + options = parse_args() + main(options, stdout) + + _script_io() diff --git a/ProberControl/prober/instruments/PIXe_4322.py b/ProberControl/prober/instruments/PIXe_4322.py new file mode 100644 index 0000000..e0b2868 --- /dev/null +++ b/ProberControl/prober/instruments/PIXe_4322.py @@ -0,0 +1,78 @@ +""" Driver for NI PIXe_4322 8 channel Power Supply + + Allows for user interfacing directly, as well as ProberControl API + + see https://probercontrol.github.io/ProberControl/source/howto/addNewInstrument.html for + more info +""" +import sys +import argparse + +import nidaqmx + +class CustomFormatter(argparse.RawDescriptionHelpFormatter, + argparse.ArgumentDefaultsHelpFormatter): + pass + +def parse_args(args=sys.argv[1:]): + """Parse arguments.""" + parser = argparse.ArgumentParser( + description=sys.modules[__name__].__doc__, + formatter_class=CustomFormatter) + + g = parser.add_argument_group("driver settings") + g.add_argument("address", metavar="gpib_address", + type=str, + help="The address of the device to be controlled") + g.add_argument("channel", metavar="device_channel", + type=str, + help="The channel to be controlled for the device") + a = parser.add_argument_group("driver actions") + a.add_argument("function", metavar="function_name", + type=str, + help="The driver function to be run") + a.add_argument("--value", metavar="function value", + type=str, + help="The value to be set for the function") + + return parser.parse_args(args) + +class PIXe_4322(object): + def __init__(self, rm, address='PXI1Slot2', channel='ao0', **kwargs): + self.address = address + self.channel = channel + self.set_voltage(0) + self.voltage = 0 + return + + def set_voltage(self,value): + with nidaqmx.Task() as task: + print(self.address) + task.ao_channels.add_ao_voltage_chan(self.address + '/' + self.channel) + task.write(value) + task.stop() + + def retreive_voltage(self): + return self.voltage + + def whoAmI(self): + return 'PowerSupply' + +def main(options, stdout): + device = PIXe_4322(options.address,options.channel) + if hasattr(PIXe_4322, options.function): + if isempty(options.value): + value = getattr(device, options.function) + else: + value = getattr(device, options.function)(options.value) + print('Function: %s' % options.function) + else: + print("Function does not exist.") + +if __name__ == '__main__': + def _script_io(): + from sys import argv, stdout + options = parse_args() + main(options, stdout) + + _script_io() diff --git a/ProberControl/prober/instruments/PSY_201.py b/ProberControl/prober/instruments/PSY_201.py new file mode 100644 index 0000000..d008fac --- /dev/null +++ b/ProberControl/prober/instruments/PSY_201.py @@ -0,0 +1,94 @@ +""" Driver for NI PIXe-4322 6 channel SMU + + Allows for user interfacing directly, as well as ProberControl API + + see https://probercontrol.github.io/ProberControl/source/howto/addNewInstrument.html for + more info +""" +import sys +import visa +import argparse + +import nidcpower + +class CustomFormatter(argparse.RawDescriptionHelpFormatter, + argparse.ArgumentDefaultsHelpFormatter): + pass + +def parse_args(args=sys.argv[1:]): + """Parse arguments.""" + parser = argparse.ArgumentParser( + description=sys.modules[__name__].__doc__, + formatter_class=CustomFormatter) + + g = parser.add_argument_group("driver settings") + g.add_argument("address", metavar="gpib_address", + type=str, + help="The address of the device to be controlled") + a = parser.add_argument_group("driver actions") + a.add_argument("function", metavar="function_name", + type=str, + help="The driver function to be run") + a.add_argument("--value", metavar="function value", + type=str, + help="The value to be set for the function") + + return parser.parse_args(args) + +class PSY_201(object): + def __init__(self, res_manager, address='PXI1Slot2', **kwargs): + self.address = address + self.gpib = res_manager.open_resource(address) + self.gpib.clear() + self.gpib.write(':CONT:DIS') + self.track = False + + def set_SOP(self,s1,s2,s3): + self.track = False + self.gpib.write(':CONT:SOP'+','.join([str(s1),str(s2),str(s3)])) + + def set_SOP_type(self,type): + self.track = False + self.gpib.write(':CONT:TSC:TYPE'+str(int(type))) + + def measure_polarization(self): + try: + return self.gpib.query(':MEAS:SOP?') + except: + return self.gpib.read() + + def toggle_track_polarization(self): + if self.track: + self.gpib.write(':CONT:DIS') + else: + self.gpib.write(':CONT:ENAB') + self.track = not self.track + + def measure_power(self): + try: + return self.gpib.query(':MEAS:POW?') + except: + return self.gpib.read() + + def whoAmI(self): + return 'PolSynthesizer' + +def main(options, stdout): + #import visa + device = PIXe_1084(options.address,options.channel) + if hasattr(PIXe_1084, options.function): + if isempty(options.value): + value = getattr(device, options.function) + else: + value = getattr(device, options.function)(options.value) + print('Function: %s' % options.function) + else: + print("Function does not exist.") + +if __name__ == '__main__': + def _script_io(): + from sys import argv, stdout + options = parse_args() + main(options, stdout) + + _script_io() diff --git a/ProberControl/prober/instruments/__init__.py b/ProberControl/prober/instruments/__init__.py index e5e95b4..99fea03 100644 --- a/ProberControl/prober/instruments/__init__.py +++ b/ProberControl/prober/instruments/__init__.py @@ -36,7 +36,15 @@ 'TL2500PowerDummy', 'TL2500MultiDummy', 'AgilentE3643A', - 'TektronixCSA8000' + 'TektronixCSA8000', + 'PIXe_4140', + 'PIXe_4322', + 'PSY_201', + 'GenPhotPCD104', + 'Keysight8164B_PowerMeter', + 'Keysight8164B_Laser', + 'EXFOT100HP_Laser', + 'EXFOT100_Laser' ] pipe_instrument_groups = { diff --git a/ProberControl/prober/procedures/Measure.py b/ProberControl/prober/procedures/Measure.py index 1c003aa..4f375f7 100644 --- a/ProberControl/prober/procedures/Measure.py +++ b/ProberControl/prober/procedures/Measure.py @@ -10,6 +10,20 @@ # Getting Global_MeasureHandler (singleton)instance; Do not change this! gh = g() + +def testPM(maitre): + + laser = gh.get_instrument('Laser') + print "laser "+str(laser) + #pm = gh.get_instrument_triggered_by(laser, 'PowerMeter') + pm = gh.get_instrument('PowerMeter') + print "PM "+str(pm) + gh.connect_instruments(laser, pm) + data = pm.get_power(float(1550)) + print data + + + def test(maitre,data): pl = NBPlot() data = [data,data,data] @@ -49,33 +63,35 @@ def get_o_spectrum_OSA(maitre,start, stop, step, result_path = 0): return DataList -def get_o_spectrum_PowerMeter(maitre,start, stop, step, channels, result_path = 0): +def get_o_spectrum_triggered_PowerMeter(maitre,start, stop, step, channels = 1, result_path = 0): start = float(start) stop = float(stop) step = float(step) - + + laser = gh.get_instrument('Laser') + laser.sweepWavelengthsContinuousTriggerOut(start, stop, step, speed = 5) + channels = int(channels) - sweepWidth = stop-start - sampleNumber = sweepWidth/(step) + 1 + sampleNumber = laser.numberOfTriggers() pms = [] for i in xrange(channels): pm = gh.get_instrument('PowerMeter', additional=True) - pm.config_meter(-30) + pm.config_meter(10) pm.prep_measure_on_trigger(sampleNumber) pms.append(pm) + laser.startSweep() - - t0 = time.time() - osa = gh.get_instrument('OSA') - OSAData = osa.get_o_spectrum(start, stop, step) - - time.sleep(3) - - AllDataList = [OSAData] + while laser.checkSweepStatus() == False: + pass + + laser.reset() + #AllDataList = [OSAData] + AllDataList = [] + for i in xrange(channels): PowerList = pms[i].get_result_from_log(sampleNumber) @@ -90,18 +106,20 @@ def get_o_spectrum_PowerMeter(maitre,start, stop, step, channels, result_path = DataList.append([start+j*step, power]) #DataList.append([start+j*step, PowerList[j]]) pl = NBPlot() - pl.plot(DataList,'Optical Spectrum for Channel ' + str(i),'Wavelength [nm]','Measured Power [dBm]') + pl.plot(DataList,'Optical Spectrum for Channel ' + str(i+1),'Wavelength [nm]','Measured Power [dBm]') AllDataList.append(DataList) #pl.plot(OSAData,'Optical Spectrum for OSA', 'Wavelength [nm]','Measured Power [dBm]') - + + + pm.reset() + if result_path != 0: - _write_data(OSAData,str(result_path)+'_OSA.txt') - for i in range(len(AllDataList)): - _write_data(AllDataList[i],str(result_path)+'_PM'+str(i)+'.txt') - + #_write_data(OSAData,str(result_path)+'_OSA.txt') + for i in range(len(AllDataList)): + DataIO.writeData(result_path,AllDataList,'get_o_spectrum') return AllDataList @@ -110,24 +128,32 @@ def get_current(maitre): data = dc.get_current() return data -def get_o_spectrum(maitre,start,stop,step,result_path=0): +def get_o_spectrum_manual_PowerMeter(maitre,start,stop,step,result_path=0): laser = gh.get_instrument('Laser') - pm = gh.get_instrument_triggered_by(laser, 'PowerMeter') + print "laser "+str(laser) + #pm = gh.get_instrument_triggered_by(laser, 'PowerMeter') + pm = gh.get_instrument('PowerMeter') + print "PM "+str(pm) + gh.connect_instruments(laser, pm) init_wavelength = float(laser.getwavelength()) - laser.sweepWavelengthsTriggerSetup(float(start),float(stop),float(step)) + laser.sweepWavelengthsManual(float(start),float(stop),float(step)) DataList = [] + + print ("Sweeping"), for x in np.arange(float(start),float(stop)+float(step),float(step)): - laser.trigger() - DataList.append([x,pm.get_o_power(x,True)]) + print ("."), + laser.step() + DataList.append([x, pm.get_power(x)]) + print ("Sweeping Complete") laser.setwavelength(init_wavelength) - pm.get_power(init_wavelength,channel) + pm.get_power(init_wavelength) pl = NBPlot() pl.plot( diff --git a/ProberControl/prober/scripts/LPR_test.scr b/ProberControl/prober/scripts/LPR_test.scr new file mode 100644 index 0000000..d993514 --- /dev/null +++ b/ProberControl/prober/scripts/LPR_test.scr @@ -0,0 +1,13 @@ +#Measurement Name +Laser power reading +#structure +basic +#procedure +Measure +#Function +get_o_spectrum_triggered_PowerMeter +#Arguments +1550 1560 0.1 1 .\test_jd + + + diff --git a/ProberControl/results b/ProberControl/results new file mode 100644 index 0000000..c6a7c0f --- /dev/null +++ b/ProberControl/results @@ -0,0 +1,13 @@ +##get_o_spectrum_0: +1550.0 5.12794365243 +1551.0 5.14616146384 +1552.0 5.15866391478 +1553.0 5.17285682962 +1554.0 5.18512742503 +1555.0 5.19459883212 +1556.0 5.20679939523 +1557.0 5.21124060407 +1558.0 5.23382057348 +1559.0 5.25570435147 + + diff --git a/ProberControl/results.csv b/ProberControl/results.csv new file mode 100644 index 0000000..48d4fc7 --- /dev/null +++ b/ProberControl/results.csv @@ -0,0 +1,53 @@ +##get_o_spectrum_0: +1550.0 5.12668137811 +1551.0 5.14497560509 +1552.0 5.15673051232 +1553.0 5.17130753887 +1554.0 5.18419551342 +1555.0 5.19333743572 +1556.0 5.2059694028 +1557.0 5.2106786189 +1558.0 5.23285904856 +1559.0 5.25470363441 + + +##get_o_spectrum_0: +1550.0 6.0060122624 +1550.1 6.01696394509 +1550.2 6.02818939805 +1550.3 6.03239304939 +1550.4 6.04165566533 +1550.5 6.05048885929 +1550.6 6.05856844723 +1550.7 6.06732933053 +1550.8 6.07410390875 +1550.9 6.0781585997 +1551.0 6.08651559448 +1551.1 6.09205545795 +1551.2 6.0934771056 +1551.3 6.10417945774 +1551.4 6.11807945357 +1551.5 6.1325717397 +1553.0 -125.024355372 +1553.3 -232.29991543 +1553.5 -81.4968738708 +1553.8 45.776915136 +1554.1 -235.972561985 +1554.3 -282.673661258 +1555.0 -226.335678259 +1555.6 -201.096373707 +1555.8 -118.909057144 +1556.5 -344.588786861 +1556.7 -172.264793699 +1556.8 -55.6460362626 +1557.4 -283.31060688 +1557.6 -92.6967707225 +1557.7 -108.464662109 +1558.5 -243.062946221 +1558.8 -298.251076646 +1558.9 -147.93267167 +1559.5 -118.973441716 +1559.6 -239.630118139 +1559.8 -221.963300335 + + diff --git a/ProberControl/setup/requirements.txt b/ProberControl/setup/requirements.txt index 0eed04b..7a1f8e6 100644 --- a/ProberControl/setup/requirements.txt +++ b/ProberControl/setup/requirements.txt @@ -61,4 +61,6 @@ widgetsnbextension==2.0.0 win-unicode-console==0.5 opencv-python==3.2.0.7 tqdm==4.11.2 -setuptools==18.5 \ No newline at end of file +setuptools==18.5 +nidcpower==1.1.2 +nidaqmx==0.5.7 \ No newline at end of file