├── .gitignore ├── README.md ├── data ├── __init__.py ├── sweepobj.py └── wvlsweep.py ├── dev ├── IQ_FBL │ ├── IQ.py │ ├── LabNotebooks_DifferentTests │ │ ├── Dec07_biastest.ipynb │ │ ├── Dec10_alignmentOSA.ipynb │ │ ├── Dec11_IQ_data.ipynb │ │ ├── Dec12_new_chip.ipynb │ │ ├── Resonance_Alignment_Algorithm.ipynb │ │ └── Resonance_alignment.ipynb │ ├── MRM.py │ ├── QAM_RAA.ipynb │ ├── QAM_RAA_v2.ipynb │ ├── algo.py │ ├── backup │ │ ├── FBL_IQ_Jan17.rar │ │ ├── QAM_RAA_Backup_jan4.ipynb │ │ ├── RTA │ │ │ ├── RTA.zip │ │ │ └── Resonance_Alignment_Algorithm.ipynb │ │ └── backup_17jan.rar │ ├── spectrum_vs_bias.ipynb │ ├── spectrum_vs_bias2.ipynb │ └── test_ILX.ipynb ├── MRF_FBL │ ├── Algo.py │ ├── MRF.py │ ├── __init__.py │ ├── jupyter │ │ ├── Console.py │ │ └── full_analysis.py │ └── scripts │ │ ├── resistancetest.py │ │ └── sweepcentralwvl.py ├── __init__.py ├── autoEdge │ ├── autoEdgeMain.py │ └── main.py ├── laserServer │ ├── app-client.py │ ├── app-server.py │ └── lib.py └── misc │ ├── LUNA_OVA5000.py │ ├── fiberarray.py │ ├── filedialog.py │ └── qontrolGUI.py ├── instruments ├── Agilent_E3631A.py ├── Agilent_E3646A.py ├── CoBriteDX1.py ├── ILX_LDT5910B.py ├── Instrument_pyvisa.py ├── JDS_SB_Switch.py ├── Keithley_2612B.py ├── TCPinterface.py ├── __init__.py ├── hp816x_instr.py ├── hp816x_instr_py3.py ├── ova5000.py ├── qontrol.py ├── smarPod │ ├── GUI_gettingstarted_main.py │ ├── SmarPodClass.py │ ├── SmarPodFrame.py │ ├── SmarPodParameters.py │ ├── __init__.py │ ├── func.py │ ├── lib │ │ ├── FTChipID.dll │ │ ├── MCSControl.dll │ │ ├── SmarActIO.dll │ │ ├── SmarPod.dll │ │ └── SmarPod.lib │ └── smaract.py └── utils.py ├── main.py ├── manuals ├── Agilent_81536A.pdf ├── Agilent_81600B.pdf ├── Agilent_81640B.pdf ├── Agilent_816x_PG.pdf ├── Agilent_816x_UG.pdf ├── Agilent_E3631A.pdf ├── Agilent_E3646A.pdf ├── Anritsu_MS9740A_PG.pdf ├── Anritsu_MS9740A_UG.pdf ├── EXFO_PM-1600_PG.pdf ├── EXFO_T100S-HP_PG.pdf ├── EXFO_T100S-HP_UG.pdf ├── ILX_LDT-5910B.pdf ├── JDS_SBOSwitch.pdf ├── Keithley_2612B.pdf ├── Luna_OVA5000.pdf └── SHF_S807.pdf ├── misc ├── WavelengthSweepTemplateNotebook.ipynb ├── agilent_control.ipynb └── testqontrol.py └── utilitaries ├── AddDropOVA.py ├── IVcurves.py ├── __init__.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX trash file 2 | .DS_Store 3 | .icloud 4 | .vscode 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ULPythonLab 2 | Various codes used to interact remotely with the instruments in the laboratory written in Python. 3 | 4 | # References 5 | * Info on the automated probe station used at UBC can be found [here!](https://siepic.ubc.ca/probestation) 6 | * Matlab code for the Probe station used at UBC can be found [here!](https://www.dropbox.com/s/dl/skhjntjs90sjtv9/SiPhoTestBench.zip) 7 | * Python based automated probe station that is used at the University of British Columbia (UBC). The source code for the probe station can be found [here!](https://github.com/lukasc-ubc/pyOptomip) 8 | * Introduction information on laboratory automation using python that was presented at OFC2019. [This repo](https://gitlab.com/python4photonics/ofcshortcourse) contains jupyter notebooks to get you started with lab automation. 9 | * [Qontrol Python API](https://qontrol.co.uk/getting-started-with-the-python-api/) 10 | 11 | # How to use python 12 | 1. Download the Anaconda Distribution which contains all the data science packages that you will need. [here!](https://www.anaconda.com) 13 | 2. Download Sublime text which is a lightweight text editor that will allow you to easily build your code. [here!](https://www.sublimetext.com) 14 | 3. Install the Conda plugin for sublime text. Follow the procedure [here!](https://docs.anaconda.com/anaconda/user-guide/tasks/integration/sublime/) 15 | 4. Write some code! 16 | 17 | # Lab instruments included 18 | For all these instruments, a class is already available and it can be found it the Instruments subdirectory. 19 | * Anristu MS9740A Optical Spectrum Analyzer 20 | * Agilent/Keysight E3631A Triple Output DC Power Supply 21 | * Agilent E3646A Dual Output DC Power Supply 22 | * EXFO PM-1600 Series High-Speed Power Meter 23 | * EXFO T100S-HP Waveleneght tunable laser diode source (fka Photonetics TUNICS-BT) 24 | * IDPhotonics CoBriteDX1 Laser 25 | * ILX LDT-5910B Thermoelectric Temperature Controller 26 | * JDS Uniphase SB Series Fiber Optic Switch 27 | * Keithley 2612B System SourceMeter Instrument 28 | * Luna Technologies OVA5000 Optical Vector Analyzer 29 | * Qontrol Systems Q8iv and Q8b Driver Modules 30 | * HP/Agilent/Keysight 816x Lightwave Measurement System 31 | 32 | # Drivers 33 | * Agilent/Keithley/HP Lightwave Measurement System 816x [here](https://www.keysight.com/main/software.jspx?ckey=112417&lc=eng&cc=CA&nid=-11143.0.00&id=112417) -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/data/__init__.py -------------------------------------------------------------------------------- /data/sweepobj.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file implements the sweepobj class. 3 | This object is a Wavelength Sweep datafile containing all the relevant information fields and data fields to handle and archive. 4 | 5 | TODO : Make a general class 'pickleWizard' that can be used to pickle an object of any type. 6 | TODO : Make the 'sweepobj' a children of 'pickleWizard'. 7 | TODO : Make a GUI for opening those files with a file explorer 8 | 9 | Author : Simon Belanger-de Villers 10 | Created : April 18th 2019 11 | Modified : November 6th 2019 12 | 13 | """ 14 | 15 | import numpy as np 16 | import matplotlib.pyplot as plt 17 | import matplotlib.ticker as ticker 18 | import pickle 19 | from pprint import pprint 20 | 21 | class sweepobj(object): 22 | """ 23 | Agilent Wavelength Sweep object datafile 24 | 25 | Contains several information fields: 26 | 27 | filename : 28 | aquisition date : 29 | device : 30 | chip name : 31 | detector_1_name : 32 | detector_2_name : 33 | wavelength_start : 34 | wavelength_stop : 35 | wavelength_step : 36 | info : 37 | 38 | Contains several data fields: 39 | 40 | wavelength : 41 | detector_1 : 42 | detector_2 : 43 | 44 | Extra : 45 | 46 | Normalisation data fields 47 | detector_1_norm: 48 | detector_2_norm: 49 | calibrated : (True or False) 50 | 51 | Visualisation params: 52 | xlims: 53 | ylims: 54 | 55 | """ 56 | 57 | def __init__(self, filename=None): 58 | """ Constructor for the sweepobject class. 59 | 60 | If the filename is specified, the constructor will load the attributes of the datafile 61 | 62 | example : sweepobj().show() 63 | this will plot the sweep contained in the datafile .pickle 64 | """ 65 | if filename != None: 66 | self.load(filename) 67 | 68 | def load_from_textfile_agilent(self, filename): 69 | """ Open a textfile in the format it is save by the Agilent Mainframe and store the data in the 70 | sweepobject. 71 | """ 72 | self.wavelength, self.detector_1, self.detector_2 = np.loadtxt(filename) 73 | 74 | def show(self): 75 | """ Plot the data in the sweepobject. """ 76 | 77 | plt.figure(figsize=(10, 4)) 78 | ax = plt.axes() 79 | #ax.xaxis.set_major_locator(ticker.MultipleLocator(5)) 80 | #ax.xaxis.set_minor_locator(ticker.MultipleLocator(1)) 81 | #ax.yaxis.set_major_locator(ticker.MultipleLocator(10)) 82 | #ax.yaxis.set_minor_locator(ticker.MultipleLocator(2)) 83 | ax.tick_params(which='major', direction='inout', length=8, width=2, colors='k', 84 | labelsize=18) 85 | ax.tick_params(which='minor', direction='in', length=4, width=1, colors='k', 86 | labelsize=18) 87 | 88 | plt.plot(self.wavelength * 1e9, self.detector_1, label="Through port", color="blue", linestyle='dashed') 89 | plt.plot(self.wavelength * 1e9, self.detector_2, label="Drop port", color="red") 90 | plt.xlabel("Wavelength [nm]", fontsize=18) 91 | plt.ylabel("Transmission [dB]", fontsize=18) 92 | plt.xlim(self.xlims) 93 | plt.ylim(self.ylims) 94 | plt.legend(loc='upper right') 95 | plt.show() 96 | 97 | def save(self, filename): 98 | """ Save the content of a sweepobjct to a datafile. """ 99 | print('Saving ' + filename + '.pickle ...') 100 | with open(filename + '.pickle', 'wb') as f: 101 | pickle.dump(self.__dict__, f, 2) 102 | 103 | def load(self, filename): 104 | """ Load a sweepobject data from a datafile. """ 105 | print('Loading ' + filename + '.pickle ...') 106 | with open(filename + '.pickle', 'rb') as f: 107 | self.__dict__.update(pickle.load(f, encoding='latin1')) 108 | 109 | def info(self): 110 | """ Function that explicits all the parameters of the datafile. """ 111 | pprint(self.__dict__, indent=2) 112 | 113 | def calibrate(self, calibrationPort1, calibrationPort2): 114 | """ Plot the data stored in the sweepobject but substract to it data stored in another sweepobj. """ 115 | self.detector_1 = self.detector_1 - calibrationPort1 116 | self.detector_2 = self.detector_2 - calibrationPort2 117 | 118 | """ 119 | Extra functions used by the previous instance of this code. 120 | """ 121 | 122 | def normalise_data(filename, align_filename): 123 | """Normalise the data with alignment marks.""" 124 | wavelength, power_1, power_2 = load_spectrum(filename) 125 | wavelength_align, power_1_align, power_2_align = load_spectrum(align_filename) 126 | plot_spectrum(wavelength, power_1-power_1_align, power_2-power_2_align) 127 | 128 | def combine_alignment(align1_filename, align2_filename, merge_filename): 129 | """Combine both alignment marks.""" 130 | #alignment mark 1 131 | wavelength, align1_det1, align1_det2 = load_spectrum(align1_filename) 132 | # alignment mark 2 133 | wavelength, align2_det1, align2_det2 = load_spectrum(align2_filename) 134 | # Combine and save 135 | np.savetxt(merge_filename,(wavelength, align1_det1, align2_det2)) 136 | 137 | if __name__ == "__main__": 138 | 139 | #1) load the raw data in textfile and save it as a sweepobj in a .pickle file 140 | sobj1 = sweepobj() 141 | sobj1.load_from_textfile_agilent('01 Passive_Response/alignment/02_1540_1560_0,001.txt') 142 | sobj1.device = 'Alignment mark 2' 143 | sobj1.wavelength_start = 154e-9 144 | sobj1.wavelength_stop = 1560e-9 145 | sobj1.wavelength_step = 0.001e-9 146 | sobj1.xlims = [1540, 1560] 147 | sobj1.ylims = [-80,-20] 148 | sobj1.info = 'Spectrum of alignment mark #2 zoomed.' 149 | sobj1.show() 150 | sobj1.save('01 Passive_Response/alignment/pickle/align2_zoom') 151 | 152 | -------------------------------------------------------------------------------- /data/wvlsweep.py: -------------------------------------------------------------------------------- 1 | from data.sweepobj import sweepobj 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import os 5 | 6 | # TODO : make this an agilent_aquire method of the sweepobj 7 | 8 | def wvlsweep(LMS, data_dir, sweepPower=0, wvlStart=1540.0e-9, wvlStop=1570.0e-9, wvlStep=0.02e-9, plot_det1 = True, plot_det2 = True, filename=None): 9 | """ 10 | Perform a wavelength sweep over the specified range. 11 | 12 | INPUTS : 13 | LMS : hp816x object for remote control of the Lightwave Measurement System 14 | data_dir : Directory where the data is stored 15 | wvlStart : Wavelength sweep start wavelength [m] 16 | wvlStop : Wavelength sweep stop wavelength [m] 17 | wvlStep : Wavelength sweep step wavelength [m] 18 | plot_det1 : Plot the power for detector 1 (True or False) 19 | plot_det2 : Plot the power for detector 2 (True or False) 20 | filename : name of the datafile 21 | """ 22 | 23 | # Set sweep parameters 24 | LMS.sweepUnit = 'dBm' 25 | LMS.sweepLaserOutput = 'lowsse' # lowsse ou highpower 26 | LMS.sweepSpeed = '0.5nm' 27 | LMS.sweepPower = sweepPower 28 | LMS.sweepNumScans = 1 29 | LMS.sweepStartWvl = wvlStart 30 | LMS.sweepStopWvl = wvlStop 31 | LMS.sweepStepWvl = wvlStep 32 | LMS.sweepInitialRange = 0 33 | LMS.sweepRangeDecrement = 0 34 | LMS.sweepClipLimit = -100 35 | 36 | # Perform the sweep 37 | wvl_sweep,pow_sweep = LMS.sweep() 38 | 39 | # Plot the results 40 | f = plt.figure() 41 | if plot_det1 == True: 42 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0], label='Detector1') 43 | if plot_det2 == True: 44 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[1], label='Detector2') 45 | if plot_det1 or plot_det2: 46 | plt.xlabel('Wavelength (nm)') 47 | plt.ylabel('Power (dBm)') 48 | plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 49 | plt.show() 50 | 51 | # Save the results as a sweepobj 52 | if filename == None: 53 | filename = input("Enter the name of the file:") 54 | 55 | if not(filename==""): 56 | 57 | # Device/Sweep Information 58 | swobj = sweepobj() 59 | swobj.filename = "" 60 | swobj.device = "" 61 | swobj.info = "" 62 | swobj.wavelength_start = LMS.sweepStartWvl 63 | swobj.wavelength_stop = LMS.sweepStopWvl 64 | swobj.wavelength_step = LMS.sweepStepWvl 65 | swobj.xlims = [LMS.sweepStartWvl*1e9, LMS.sweepStopWvl*1e9] # Defaults 66 | swobj.ylims = [LMS.sweepClipLimit, 0] # Defaults 67 | 68 | # Put the data 69 | swobj.wavelength = wvl_sweep 70 | swobj.detector_1 = pow_sweep.transpose()[0] 71 | swobj.detector_2 = pow_sweep.transpose()[1] 72 | 73 | # Save the datafile 74 | swobj.save(data_dir + filename) 75 | print("Saving the data to " + data_dir + filename + " ...") 76 | -------------------------------------------------------------------------------- /dev/IQ_FBL/IQ.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import time 4 | from MRM import MRM 5 | import winsound 6 | 7 | class IQ(object): 8 | """ Class for a single channel IQ modulator. """ 9 | 10 | def __init__(self, SMU_I, SMU_Q): 11 | 12 | # Create both Microring modulator objects 13 | self.I = MRM(SMU_I,'I') 14 | self.Q = MRM(SMU_Q,'Q') 15 | 16 | # Turn off the filters 17 | self.filter_off() 18 | 19 | def set_voltage(self, V): 20 | 21 | self.V = V 22 | self.I.V = V 23 | self.Q.V = V 24 | 25 | def calibrate(self, auto=False): 26 | if auto == True: 27 | user_input = "y" 28 | else: 29 | user_input = raw_input("Calibration requires that no optical power is present on the chip. Proceed? (y/n)\n") 30 | if user_input == "y": 31 | print("Calibration in progress ...\n") 32 | self.I.SMU.output_on() 33 | self.Q.SMU.output_on() 34 | 35 | self.I.Vd, self.Q.Vd, self.I.Id, self.Q.Id = self.sweep(self.V) 36 | 37 | self.I.SMU.output_on() 38 | self.Q.SMU.output_on() 39 | print("Calibration finished!\n") 40 | else: 41 | print("Calibration aborted.\n") 42 | 43 | def autocalibrate(self, laser): 44 | """Perform a calibration when the laser is controlled remotely.""" 45 | 46 | # Calibration 47 | laser.laser_off() 48 | time.sleep(3) 49 | self.calibrate(auto=True) 50 | 51 | # With laser power on 52 | laser.laser_on() 53 | time.sleep(15) 54 | Imin, Qmin = self.sweep_norm(auto=True) 55 | 56 | # Calibration complete 57 | print("Autocalibration complete.") 58 | winsound.Beep(2500, 200) 59 | winsound.Beep(2500, 200) 60 | 61 | return Imin, Qmin 62 | 63 | 64 | def sweep(self,V): 65 | """Sweep the voltage and measure the photocurrent.""" 66 | i_I = [] 67 | i_Q = [] 68 | for v in V: 69 | i_I.append(self.I.update(v)) 70 | i_Q.append(self.Q.update(v)) 71 | plt.plot(V,np.asarray(i_I)*1e3, label = self.I.name) 72 | plt.plot(V,np.asarray(i_Q)*1e3, label = self.Q.name) 73 | plt.xlabel("Voltage [V]") 74 | plt.ylabel("Current [mA]") 75 | plt.legend() 76 | plt.show() 77 | return V, V, i_I, i_Q 78 | 79 | def update_norm(self, V, delay = 0.5): 80 | 81 | # Apply voltage 82 | self.apply_voltage(V) 83 | 84 | time.sleep(delay) 85 | 86 | # Measure the current 87 | I = self.measure_current()[0] 88 | 89 | # Remove the resistive component 90 | V_closest = min(self.Vd, key=lambda x:abs(x-V)) 91 | In = I - self.Id[self.Vd.index(V_closest)] 92 | 93 | return In 94 | 95 | def measure_Id(self, index): 96 | 97 | self.apply_voltage(self.V[index]) 98 | 99 | V_closest = min(self.Vd, key=lambda x:abs(x-self.V[index])) 100 | Id = self.measure_current()[0] - self.Id[self.Vd.index(V_closest)] 101 | return Id 102 | 103 | def sweep_norm(self, auto=False): 104 | """Sweep the voltage and measure the photocurrent.""" 105 | if auto == False: 106 | raw_input("Turn on the laser pefore proceeding! Press any key to continue.") 107 | self.I.SMU.output_on() 108 | self.Q.SMU.output_on() 109 | i_I = [] 110 | i_Q = [] 111 | for v in self.V: 112 | i_I.append(self.I.update_norm(v, 0)) 113 | i_Q.append(self.Q.update_norm(v, 0)) 114 | plt.plot(range(len(self.V))[2:],np.asarray(i_I)[2:]*1e6, label = self.I.name) 115 | plt.plot(range(len(self.V))[2:],np.asarray(i_Q)[2:]*1e6, label = self.Q.name) 116 | plt.xlabel("Voltage [V]") 117 | plt.ylabel("Current [uA]") 118 | plt.legend() 119 | plt.show() 120 | self.I.SMU.output_off() 121 | self.Q.SMU.output_off() 122 | return i_I.index(min(i_I)), i_Q.index(min(i_Q)) 123 | 124 | def filter_off(self): 125 | """Turn the filters for the measured current to the off state.""" 126 | # Turn off filters 127 | self.I.SMU.filter_off() 128 | self.Q.SMU.filter_off() 129 | print("Filters are now off.") 130 | 131 | def filter_on(self, count, filtertype): 132 | """Apply the given filter for both microrings.""" 133 | 134 | #Set filter parameters 135 | self.I.SMU.set_filter(count, filtertype) 136 | self.Q.SMU.set_filter(count, filtertype) 137 | 138 | # Turn filters on 139 | self.I.SMU.filter_on() 140 | self.Q.SMU.filter_on() 141 | print("Filters are now on.") -------------------------------------------------------------------------------- /dev/IQ_FBL/LabNotebooks_DifferentTests/Dec07_biastest.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 14, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "\n", 10 | "from Instruments.Keithley_2612B import Keithley_2612B\n", 11 | "\n", 12 | "# DC sources objects\n", 13 | "SMU_ring1 = Keithley_2612B(25, 'a')\n", 14 | "SMU_ring2 = Keithley_2612B(25, 'b')\n", 15 | "\n", 16 | "SMU_ring1.connect()\n", 17 | "SMU_ring2.connect()" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 26, 23 | "metadata": {}, 24 | "outputs": [ 25 | { 26 | "ename": "NameError", 27 | "evalue": "name 'SMU_ring1' is not defined", 28 | "output_type": "error", 29 | "traceback": [ 30 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 31 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 32 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mSMU_ring1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moutput_off\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[0mSMU_ring2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moutput_off\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 33 | "\u001b[1;31mNameError\u001b[0m: name 'SMU_ring1' is not defined" 34 | ] 35 | } 36 | ], 37 | "source": [ 38 | "SMU_ring1.output_off()\n", 39 | "SMU_ring2.output_off()" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 3, 45 | "metadata": {}, 46 | "outputs": [ 47 | { 48 | "ename": "ImportError", 49 | "evalue": "No module named Instruments.hp816x_instr", 50 | "output_type": "error", 51 | "traceback": [ 52 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 53 | "\u001b[1;31mImportError\u001b[0m Traceback (most recent call last)", 54 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mInstruments\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mhp816x_instr\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mhp816x\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mos\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0msys\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mmatplotlib\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpyplot\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mplt\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 55 | "\u001b[1;31mImportError\u001b[0m: No module named Instruments.hp816x_instr" 56 | ] 57 | } 58 | ], 59 | "source": [ 60 | "from Instruments.hp816x_instr import hp816x\n", 61 | "import os\n", 62 | "import sys\n", 63 | "import matplotlib.pyplot as plt\n", 64 | "import numpy as np\n", 65 | "\n", 66 | "class sweepobj(object):\n", 67 | " \n", 68 | " def __init__(self,LMS, data_dir=os.getcwd()):\n", 69 | " self.LMS = LMS \n", 70 | " self.data_dir = data_dir\n", 71 | " print(self.data_dir) \n", 72 | " \n", 73 | " def setup_LMS(self):\n", 74 | " \"\"\"Setup the Lightwave Measurement System for use.\"\"\"\n", 75 | " self.LMS.setAutorangeAll()\n", 76 | " \n", 77 | " def wvl_sweep(self, wvl_start=1540, wvl_stop=1570, wvl_step=0.02, plot = True, filename=None):\n", 78 | " \"\"\"Perform a wavelength sweep over the specified range.\"\"\"\n", 79 | " \n", 80 | " # Init Instrument\n", 81 | " self.LMS.sweepUnit = 'dBm'\n", 82 | " self.LMS.sweepLaserOutput = 'highpower' # lowsse ou highpower\n", 83 | " self.LMS.sweepStartWvl = wvl_start * 1e-9\n", 84 | " self.LMS.sweepStopWvl = wvl_stop * 1e-9\n", 85 | " self.LMS.sweepStepWvl = wvl_step * 1e-9\n", 86 | " self.LMS.sweepInitialRange = 0\n", 87 | " self.LMS.sweepRangeDecrement = 20\n", 88 | " self.LMS.setPWMPowerUnit(2, 0, 'dBm')\n", 89 | "\n", 90 | " #Sweep\n", 91 | " wvl_sweep,pow_sweep = self.LMS.sweep()\n", 92 | "\n", 93 | " # Turn off the laser\n", 94 | " self.setup_LMS()\n", 95 | " \n", 96 | " # Plot the results\n", 97 | " f = plt.figure()\n", 98 | " if plot == True:\n", 99 | " plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0])\n", 100 | " if plot:\n", 101 | " plt.xlabel('Wavelength (nm)')\n", 102 | " plt.ylabel('Power (dBm)')\n", 103 | " plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)\n", 104 | " plt.show()\n", 105 | " \n", 106 | " # Save the results\n", 107 | " if filename == None:\n", 108 | " filename = raw_input(\"Enter the name of the file:\")\n", 109 | " if not(filename==\"\"):\n", 110 | " complete_name = self.data_dir + '\\\\' + filename \n", 111 | " np.savetxt(complete_name + \".txt\", (wvl_sweep,pow_sweep.transpose()[0]))\n", 112 | " f.savefig(complete_name + \".pdf\")\n", 113 | " \n", 114 | " \n", 115 | "LMS = hp816x()\n", 116 | "if LMS.connected == True:\n", 117 | " LMS.disconnect()\n", 118 | "else:\n", 119 | " LMS.connect('GPIB0::20::INSTR')\n", 120 | "\n", 121 | "so = sweepobj(LMS, 'C:\\Users\\Lab\\Documents\\MRM_QAM\\Data\\dec7')\n", 122 | "so.wvl_sweep(1545,1555,0.02)\n", 123 | "\n", 124 | "LMS.disconnect()" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 2, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "name": "stdout", 134 | "output_type": "stream", 135 | "text": [ 136 | "The mainframe is: HP8164A\n", 137 | "Connected to the laser\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | " # Initialize the laser, connect it and set the sweep params\n", 143 | "\n", 144 | "from Instruments.hp816x_instr import hp816x\n", 145 | "import os\n", 146 | "import sys \n", 147 | " \n", 148 | "hp = hp816x()\n", 149 | "#hp.disconnect()\n", 150 | "hp.connect('GPIB0::20::INSTR')\n", 151 | "#hp.sweep()" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 3, 157 | "metadata": {}, 158 | "outputs": [ 159 | { 160 | "data": { 161 | "text/plain": [ 162 | "-100" 163 | ] 164 | }, 165 | "execution_count": 3, 166 | "metadata": {}, 167 | "output_type": "execute_result" 168 | } 169 | ], 170 | "source": [ 171 | "hp.readPWM(2,0)" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [] 180 | } 181 | ], 182 | "metadata": { 183 | "kernelspec": { 184 | "display_name": "Python 2", 185 | "language": "python", 186 | "name": "python2" 187 | }, 188 | "language_info": { 189 | "codemirror_mode": { 190 | "name": "ipython", 191 | "version": 2 192 | }, 193 | "file_extension": ".py", 194 | "mimetype": "text/x-python", 195 | "name": "python", 196 | "nbconvert_exporter": "python", 197 | "pygments_lexer": "ipython2", 198 | "version": "2.7.15" 199 | } 200 | }, 201 | "nbformat": 4, 202 | "nbformat_minor": 2 203 | } 204 | -------------------------------------------------------------------------------- /dev/IQ_FBL/LabNotebooks_DifferentTests/Dec12_new_chip.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "from Instruments.Keithley_2612B import Keithley_2612B\n", 11 | "from Instruments.Agilent_E3631A import Agilent_E3631A \n", 12 | "import time\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "\n", 15 | "PS = Agilent_E3631A(5, 'p6')\n", 16 | "PS.connect()" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "The mainframe is: HP8164A\n", 29 | "Connected to the laser\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "from Instruments.hp816x_instr import hp816x\n", 35 | "import os\n", 36 | "import sys \n", 37 | "\n", 38 | "hp = hp816x()\n", 39 | "#hp.disconnect()\n", 40 | "hp.connect('GPIB0::20::INSTR')\n", 41 | "#hp.sweep()\n", 42 | "hp.setAutorangeAll()" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "from Instruments.hp816x_instr import hp816x\n", 52 | "import os\n", 53 | "import sys\n", 54 | "import matplotlib.pyplot as plt\n", 55 | "import numpy as np\n", 56 | "\n", 57 | "class sweepobj(object):\n", 58 | " \n", 59 | " def __init__(self,LMS, data_dir=os.getcwd()):\n", 60 | " self.LMS = LMS \n", 61 | " self.data_dir = data_dir\n", 62 | " print(self.data_dir) \n", 63 | " \n", 64 | " def setup_LMS(self):\n", 65 | " \"\"\"Setup the Lightwave Measurement System for use.\"\"\"\n", 66 | " self.LMS.setAutorangeAll()\n", 67 | " \n", 68 | " def wvl_sweep(self, wvl_start=1540, wvl_stop=1570, wvl_step=0.02, plot = True, filename=None):\n", 69 | " \"\"\"Perform a wavelength sweep over the specified range.\"\"\"\n", 70 | " \n", 71 | " # Init Instrument\n", 72 | " self.LMS.sweepUnit = 'dBm'\n", 73 | " self.LMS.sweepLaserOutput = 'highpower' # lowsse ou highpower\n", 74 | " self.LMS.sweepStartWvl = wvl_start * 1e-9\n", 75 | " self.LMS.sweepStopWvl = wvl_stop * 1e-9\n", 76 | " self.LMS.sweepStepWvl = wvl_step * 1e-9\n", 77 | " self.LMS.sweepInitialRange = 0\n", 78 | " self.LMS.sweepRangeDecrement = 20\n", 79 | " self.LMS.setPWMPowerUnit(2, 0, 'dBm')\n", 80 | "\n", 81 | " #Sweep\n", 82 | " wvl_sweep,pow_sweep = self.LMS.sweep()\n", 83 | "\n", 84 | " # Turn off the laser\n", 85 | " self.setup_LMS()\n", 86 | " \n", 87 | " # Plot the results\n", 88 | " f = plt.figure()\n", 89 | " if plot == True:\n", 90 | " plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0])\n", 91 | " if plot:\n", 92 | " plt.xlabel('Wavelength (nm)')\n", 93 | " plt.ylabel('Power (dBm)')\n", 94 | " plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)\n", 95 | " plt.show()\n", 96 | " \n", 97 | " # Save the results\n", 98 | " if filename == None:\n", 99 | " filename = raw_input(\"Enter the name of the file:\")\n", 100 | " if not(filename==\"\"):\n", 101 | " complete_name = self.data_dir + '\\\\' + filename \n", 102 | " np.savetxt(complete_name + \".txt\", (wvl_sweep,pow_sweep.transpose()[0]))\n", 103 | " f.savefig(complete_name + \".pdf\")" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "LMS = hp816x()\n", 113 | "if LMS.connected == True:\n", 114 | " LMS.disconnect()\n", 115 | "else:\n", 116 | " LMS.connect('GPIB0::20::INSTR')\n", 117 | "\n", 118 | "so = sweepobj(LMS, 'C:\\Users\\Lab\\Documents\\MRM_QAM\\Data\\dec7')\n", 119 | "so.wvl_sweep(1540,1552,0.1)\n", 120 | "\n", 121 | "LMS.disconnect()" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 8, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "PS.source_voltage(1.4)\n" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 9, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "import numpy as np\n", 140 | "from Instruments.Keithley_2612B import Keithley_2612B\n", 141 | "from Instruments.Agilent_E3631A import Agilent_E3631A \n", 142 | "import time\n", 143 | "import matplotlib.pyplot as plt\n", 144 | "\n", 145 | "# DC sources objects\n", 146 | "SMU_I = Keithley_2612B(0, 25, 'a')\n", 147 | "SMU_Q = Keithley_2612B(0, 25, 'b')\n", 148 | "\n", 149 | "# DC objects\n", 150 | "#DC_I = Agilent_E3631A(1, '6')\n", 151 | "#DC_Q = Agilent_E3631A(5, '6')\n", 152 | "\n", 153 | "DC_I = Keithley_2612B(1, 26, 'a')\n", 154 | "DC_Q = Keithley_2612B(1, 26, 'b')\n", 155 | "\n", 156 | "PS = Agilent_E3631A(5, 'p6')\n", 157 | "\n", 158 | "SMU_I.connect()\n", 159 | "SMU_Q.connect()\n", 160 | "DC_I.connect()\n", 161 | "DC_Q.connect()\n", 162 | "PS.connect()" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 14, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [ 171 | "# TUning to 1556.2 (Real)\n", 172 | "\n", 173 | "# Set the rings reverse bias to 2V\n", 174 | "SMU_I.source_voltage(0)\n", 175 | "SMU_Q.source_voltage(0)\n", 176 | "SMU_I.output_on()\n", 177 | "SMU_Q.output_on()\n", 178 | "# Set the heaters bias to 0\n", 179 | "DC_I.source_voltage(0)#1.96v to align on the notch\n", 180 | "DC_Q.source_voltage(4) ## Away from the carrier 4V\n", 181 | "DC_I.output_on()\n", 182 | "DC_Q.output_on()\n", 183 | "\n", 184 | "# Set the phaseshift\n", 185 | "PS.source_voltage(1.4)" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [] 194 | } 195 | ], 196 | "metadata": { 197 | "kernelspec": { 198 | "display_name": "Python 2", 199 | "language": "python", 200 | "name": "python2" 201 | }, 202 | "language_info": { 203 | "codemirror_mode": { 204 | "name": "ipython", 205 | "version": 2 206 | }, 207 | "file_extension": ".py", 208 | "mimetype": "text/x-python", 209 | "name": "python", 210 | "nbconvert_exporter": "python", 211 | "pygments_lexer": "ipython2", 212 | "version": "2.7.15" 213 | } 214 | }, 215 | "nbformat": 4, 216 | "nbformat_minor": 2 217 | } 218 | -------------------------------------------------------------------------------- /dev/IQ_FBL/LabNotebooks_DifferentTests/Resonance_Alignment_Algorithm.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Resonance Alignment for a single Microring resonator using defect-mediated photo-detection\n", 8 | "\n", 9 | "Author: Simon Bélanger-de Villers\n", 10 | "\n", 11 | "Date: 13/12/2018\n", 12 | "\n", 13 | "Based on H. JayatillekaX:\n", 169 | " d_index = -1 * d_index\n", 170 | " \n", 171 | " # Actuate values\n", 172 | " Iteration.append(i)\n", 173 | " V_in.append(MRM.V[index])\n", 174 | " I_out.append(Y*1e6)\n", 175 | "\n", 176 | " # Draw the plot\n", 177 | " ax1.plot(Iteration, V_in, 'b')\n", 178 | " ax2.plot(Iteration, I_out, 'r')\n", 179 | " plt.xlim([min(Iteration),max(Iteration)])\n", 180 | " if len(Iteration)>100:\n", 181 | " plt.xlim([max(Iteration)-100,max(Iteration)])\n", 182 | " ax1.set_ylim([min(V_in),max(V_in)])\n", 183 | " ax2.set_ylim([min(I_out),max(I_out)])\n", 184 | " plt.pause(0.01)\n", 185 | " \n", 186 | " fig.tight_layout()\n", 187 | " plt.xlim([min(Iteration),max(Iteration)])\n", 188 | " fig.show()" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "## 3) Running the resonance alignment algorithm\n", 196 | "\n", 197 | "Run this section to perform alignment." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 181, 203 | "metadata": {}, 204 | "outputs": [ 205 | { 206 | "name": "stdout", 207 | "output_type": "stream", 208 | "text": [ 209 | "Calibration requires that no optical power is present on the chip. Proceed? (y/n)\n", 210 | "y\n", 211 | "Calibration in progress ...\n", 212 | "\n", 213 | "Calibration finished!\n", 214 | "\n", 215 | "Turn on the laser pefore proceeding! Press any key to continue.k\n" 216 | ] 217 | } 218 | ], 219 | "source": [ 220 | "# Initialize objects\n", 221 | "from Instruments.Keithley_2612B import Keithley_2612B\n", 222 | "Q = MRM(Keithley_2612B(1, 26, 'b'))\n", 223 | "Q.SMU.output_on()\n", 224 | "\n", 225 | "# Set the voltage range + resolution\n", 226 | "Q.V = np.linspace(0,4,400).tolist()\n", 227 | "\n", 228 | "# Calibration\n", 229 | "Q.calibrate()\n", 230 | "\n", 231 | "# Find Initial value for power \n", 232 | "Q.sweep_norm()" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 183, 238 | "metadata": {}, 239 | "outputs": [ 240 | { 241 | "name": "stderr", 242 | "output_type": "stream", 243 | "text": [ 244 | "C:\\ProgramData\\Anaconda2\\lib\\site-packages\\matplotlib\\axes\\_base.py:3471: UserWarning: Attempting to set identical bottom==top results\n", 245 | "in singular transformations; automatically expanding.\n", 246 | "bottom=2.51629072682, top=2.51629072682\n", 247 | " 'bottom=%s, top=%s') % (bottom, top))\n", 248 | "C:\\ProgramData\\Anaconda2\\lib\\site-packages\\matplotlib\\axes\\_base.py:3471: UserWarning: Attempting to set identical bottom==top results\n", 249 | "in singular transformations; automatically expanding.\n", 250 | "bottom=-102.33, top=-102.33\n", 251 | " 'bottom=%s, top=%s') % (bottom, top))\n" 252 | ] 253 | }, 254 | { 255 | "ename": "KeyboardInterrupt", 256 | "evalue": "", 257 | "output_type": "error", 258 | "traceback": [ 259 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 260 | "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", 261 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m# Running the feedback loop\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mFBL\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mQ\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m250\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m100\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 262 | "\u001b[1;32m\u001b[0m in \u001b[0;36mFBL\u001b[1;34m(MRM, I_i, d_index, N_iter)\u001b[0m\n\u001b[0;32m 19\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mIter\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 20\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 21\u001b[1;33m \u001b[0mtime\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msleep\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0.2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 22\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 23\u001b[0m \u001b[0mX\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mMRM\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmeasure_Id\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mindex\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# Measure X\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 263 | "\u001b[1;31mKeyboardInterrupt\u001b[0m: " 264 | ] 265 | } 266 | ], 267 | "source": [ 268 | "# Running the feedback loop\n", 269 | "FBL(Q, 250, 1, 100)" 270 | ] 271 | } 272 | ], 273 | "metadata": { 274 | "kernelspec": { 275 | "display_name": "Python 2", 276 | "language": "python", 277 | "name": "python2" 278 | }, 279 | "language_info": { 280 | "codemirror_mode": { 281 | "name": "ipython", 282 | "version": 2 283 | }, 284 | "file_extension": ".py", 285 | "mimetype": "text/x-python", 286 | "name": "python", 287 | "nbconvert_exporter": "python", 288 | "pygments_lexer": "ipython2", 289 | "version": "2.7.15" 290 | } 291 | }, 292 | "nbformat": 4, 293 | "nbformat_minor": 2 294 | } 295 | -------------------------------------------------------------------------------- /dev/IQ_FBL/MRM.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import time 4 | 5 | class MRM(object): 6 | """ Class for a single microring modulator. """ 7 | 8 | Vd = None 9 | Id = None 10 | 11 | LowLim = 0 12 | HiLim = 4 13 | 14 | def __init__(self, SMU, name): 15 | 16 | self.SMU = SMU 17 | 18 | # Connect instruments 19 | self.SMU.connect() 20 | self.SMU.output_off() 21 | self.name = name 22 | 23 | def apply_voltage(self, V): 24 | """Apply bias to heater.""" 25 | self.SMU.source_voltage(V) 26 | 27 | def measure_current(self): 28 | """Measure the photocurrent generated by the microring resonator using the source meter unit (SMU).""" 29 | return self.SMU.measure_current() 30 | 31 | def update(self, V): 32 | """Apply the command (heater power) and measure the output (photocurrent).""" 33 | self.apply_voltage(V) 34 | return self.measure_current()[0] 35 | 36 | def sweep(self,V): 37 | """Sweep the voltage and measure the photocurrent.""" 38 | I = [] 39 | for v in V: 40 | I.append(self.update(v)) 41 | plt.plot(V,np.asarray(I)*1e3, label = self.name) 42 | plt.xlabel("Voltage [V]") 43 | plt.ylabel("Current [mA]") 44 | plt.legend() 45 | plt.show() 46 | return V,I 47 | 48 | def calibrate(self): 49 | user_input = raw_input("Calibration requires that no optical power is present on the chip. Proceed? (y/n)\n") 50 | if user_input == "y": 51 | print("Calibration in progress ...\n") 52 | self.SMU.output_on() 53 | self.Vd, self.Id = self.sweep(self.V) 54 | self.SMU.output_off() 55 | print("Calibration finished!\n") 56 | else: 57 | print("Calibration aborted.\n") 58 | 59 | 60 | def update_norm(self, V, delay = 0.5): 61 | 62 | # Apply voltage 63 | self.apply_voltage(V) 64 | 65 | time.sleep(delay) 66 | 67 | # Measure the current 68 | I = self.measure_current()[0] 69 | 70 | # Remove the resistive component 71 | V_closest = min(self.Vd, key=lambda x:abs(x-V)) 72 | In = I - self.Id[self.Vd.index(V_closest)] 73 | 74 | return In 75 | 76 | def measure_Id(self, index): 77 | 78 | self.apply_voltage(self.V[index]) 79 | 80 | V_closest = min(self.Vd, key=lambda x:abs(x-self.V[index])) 81 | Id = self.measure_current()[0] - self.Id[self.Vd.index(V_closest)] 82 | return Id 83 | 84 | def sweep_norm(self): 85 | """Sweep the voltage and measure the photocurrent.""" 86 | raw_input("Turn on the laser pefore proceeding! Press any key to continue.") 87 | self.SMU.output_on() 88 | I = [] 89 | for v in self.V: 90 | I.append(self.update_norm(v, 0)) 91 | plt.plot(range(len(self.V)),np.asarray(I)*1e6, label = self.name) 92 | plt.xlabel("Voltage [V]") 93 | plt.ylabel("Current [uA]") 94 | plt.legend() 95 | plt.show() 96 | self.SMU.output_off() 97 | #return V,I -------------------------------------------------------------------------------- /dev/IQ_FBL/algo.py: -------------------------------------------------------------------------------- 1 | import time 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import os 5 | 6 | def IQ_FBL(IQ, indexI, indexQ, d_indexI, d_indexQ, N_iter, I_on = True, Q_on = True, filter=True, filtertype="repeat average", count=3): 7 | """Feedback look for resonance alignment.""" 8 | 9 | #Execute the code while no interruption 10 | try: 11 | 12 | # Configure options 13 | Iter = range(N_iter) 14 | Iteration, V_inI, I_outI, V_inQ, I_outQ = [], [], [], [], [] 15 | 16 | # draw the plot 17 | fig, ax1, ax2, ax3, ax4 = draw_axes() 18 | 19 | # Open the DC sources and set the filters 20 | IQ.I.SMU.output_on() 21 | IQ.Q.SMU.output_on() 22 | if filter==True: 23 | IQ.filter_on(count, filtertype) 24 | else: 25 | IQ.filter_off() 26 | 27 | # Feedback loop 28 | for i in Iter: 29 | 30 | # time.sleep(0.2) 31 | 32 | X_I = IQ.I.measure_Id(indexI) # Measure X 33 | X_Q = IQ.Q.measure_Id(indexQ) # Measure X 34 | 35 | if I_on: 36 | indexI += d_indexI # Increase the voltage 37 | if Q_on: 38 | indexQ += d_indexQ # Increase the voltage 39 | 40 | Y_I = IQ.I.measure_Id(indexI) # Measure Y 41 | Y_Q = IQ.Q.measure_Id(indexQ) # Measure Y 42 | 43 | if Y_I>X_I: 44 | d_indexI = -1 * d_indexI 45 | 46 | if Y_Q>X_Q: 47 | d_indexQ = -1 * d_indexQ 48 | 49 | # Actuate values 50 | Iteration.append(i) 51 | V_inI.append(IQ.I.V[indexI]) 52 | I_outI.append(Y_I*1e6) 53 | V_inQ.append(IQ.Q.V[indexQ]) 54 | I_outQ.append(Y_Q*1e6) 55 | 56 | # Draw the plot 57 | ax1.plot(Iteration, V_inI, 'b') # Voltage I 58 | ax2.plot(Iteration, I_outI, 'r')# Current I 59 | ax3.plot(Iteration, V_inQ, 'b') # Voltage Q 60 | ax4.plot(Iteration, I_outQ, 'r')# CUrrent Q 61 | 62 | # Set the limits for the axes 63 | ax1.set_xlim([min(Iteration),max(Iteration)+1]) 64 | if len(Iteration)>100: 65 | ax1.set_xlim([max(Iteration)-100,max(Iteration)]) 66 | #ax1.set_ylim([min(V_inI),max(V_inI)]) 67 | ax1.set_ylim([min(IQ.I.V),max(IQ.I.V)]) 68 | ax2.set_ylim([min(I_outI)-1,max(I_outI)+1]) 69 | 70 | ax3.set_xlim([min(Iteration),max(Iteration)+1]) 71 | if len(Iteration)>100: 72 | ax3.set_xlim([max(Iteration)-100,max(Iteration)]) 73 | #ax3.set_ylim([min(V_inQ),max(V_inQ)]) 74 | ax3.set_ylim([min(IQ.Q.V),max(IQ.Q.V)]) 75 | ax4.set_ylim([min(I_outQ)-1,max(I_outQ)+1]) 76 | plt.pause(0.01) 77 | 78 | #Average 79 | ax1.cla(); ax2.cla(); ax3.cla(); ax4.cla() 80 | ax1.plot([min(Iteration),max(Iteration)+1], [np.average(np.asarray(V_inI)),np.average(np.asarray(V_inI))], 'b--') 81 | ax2.plot([min(Iteration),max(Iteration)+1], [np.average(np.asarray(I_outI)),np.average(np.asarray(I_outI))], 'r--') 82 | ax3.plot([min(Iteration),max(Iteration)+1], [np.average(np.asarray(V_inQ)),np.average(np.asarray(V_inQ))], 'b--') 83 | ax4.plot([min(Iteration),max(Iteration)+1], [np.average(np.asarray(I_outQ)),np.average(np.asarray(I_outQ))], 'r--') 84 | 85 | fig.tight_layout() 86 | ax1.set_xlim([min(Iteration),max(Iteration)]) 87 | ax3.set_xlim([min(Iteration),max(Iteration)]) 88 | fig.show() 89 | 90 | # Save the data 91 | timestr = time.strftime("%Y-%m-%d@%H_%M_%S") 92 | np.savetxt('IQ_' + timestr + '.txt', (Iteration, V_inI, I_outI, V_inQ, I_outQ)) 93 | 94 | # Save the data even if the program is closed 95 | except KeyboardInterrupt: 96 | # Save the data 97 | timestr = time.strftime("%Y-%m-%d@%H_%M_%S") 98 | np.savetxt('IQ_' + timestr + '.txt', (Iteration, V_inI, I_outI, V_inQ, I_outQ)) 99 | 100 | def draw_axes(): 101 | # Configure plot I 102 | fig, (ax1, ax3) = plt.subplots(nrows=2, sharex=True) 103 | ax1.set_title('I Tuning') 104 | ax3.set_title('Q Tuning') 105 | 106 | ax1.set_xlabel('Iteration') 107 | ax1.set_ylabel('Voltage [V]', color='b') 108 | ax1.tick_params('y', colors='b') 109 | ax2 = ax1.twinx() 110 | ax2.set_ylabel('Photocurrent [uA]', color='r') 111 | ax2.tick_params('y', colors='r') 112 | 113 | # Configure plot Q 114 | ax3.set_xlabel('Iteration') 115 | ax3.set_ylabel('Voltage [V]', color='b') 116 | ax3.tick_params('y', colors='b') 117 | ax4 = ax3.twinx() 118 | ax4.set_ylabel('Photocurrent [uA]', color='r') 119 | ax4.tick_params('y', colors='r') 120 | 121 | return fig, ax1, ax2, ax3, ax4 122 | 123 | 124 | 125 | def average_measure_Id(MRR, index, count): 126 | l = [] 127 | for i in range(count): 128 | l.append(MRR.measure_Id(index)) 129 | return reduce(lambda x, y: x + y, l) / len(l) 130 | 131 | def load_IQdat(filename): 132 | """Load a convergence plot from a previous experiment.""" 133 | 134 | # draw the plot 135 | fig, ax1, ax2, ax3, ax4 = draw_axes() 136 | 137 | # Load the data from the text file 138 | Iteration, V_inI, I_outI, V_inQ, I_outQ = np.loadtxt(filename + '.txt') 139 | 140 | # Plot the data 141 | ax1.plot(Iteration, V_inI, 'b') 142 | ax2.plot(Iteration, I_outI, 'r') 143 | ax1.set_xlim([min(Iteration),max(Iteration)]) 144 | ax1.set_ylim([min(V_inI),max(V_inI)]) 145 | ax2.set_ylim([min(I_outI),max(I_outI)]) 146 | 147 | ax3.plot(Iteration, V_inQ, 'b') 148 | ax4.plot(Iteration, I_outQ, 'r') 149 | ax3.set_xlim([min(Iteration),max(Iteration)]) 150 | ax3.set_ylim([min(V_inQ),max(V_inQ)]) 151 | ax4.set_ylim([min(I_outQ),max(I_outQ)]) 152 | fig.tight_layout() 153 | fig.show() 154 | 155 | plt.savefig(filename + '.pdf') 156 | plt.close(fig) 157 | 158 | def batch_IQdat(directory): 159 | 160 | for file in os.listdir(directory): 161 | if file.endswith(".txt"): 162 | load_IQdat(os.path.splitext(file)[0]) -------------------------------------------------------------------------------- /dev/IQ_FBL/backup/FBL_IQ_Jan17.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/dev/IQ_FBL/backup/FBL_IQ_Jan17.rar -------------------------------------------------------------------------------- /dev/IQ_FBL/backup/RTA/RTA.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/dev/IQ_FBL/backup/RTA/RTA.zip -------------------------------------------------------------------------------- /dev/IQ_FBL/backup/backup_17jan.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/dev/IQ_FBL/backup/backup_17jan.rar -------------------------------------------------------------------------------- /dev/IQ_FBL/spectrum_vs_bias2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [ 10 | { 11 | "ename": "NameError", 12 | "evalue": "name 'source' is not defined", 13 | "output_type": "error", 14 | "traceback": [ 15 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 16 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 17 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 8\u001b[0m \u001b[1;31m# Script for a single bias sweep WORKS\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m def sweep_bias_shape(wvl_start=1540, wvl_stop=1560, wvl_step=0.001, DCsource=source, bias_min=0, bias_max=0.5, bias_points=2, \n\u001b[0m\u001b[0;32m 10\u001b[0m dirname = \"\\\\datatest\\\\\"):\n\u001b[0;32m 11\u001b[0m \u001b[1;34m\"\"\"\"\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 18 | "\u001b[1;31mNameError\u001b[0m: name 'source' is not defined" 19 | ] 20 | } 21 | ], 22 | "source": [ 23 | "###### the\n", 24 | "import os\n", 25 | "import numpy as np\n", 26 | "import matplotlib.pyplot as plt\n", 27 | "from matplotlib import cm\n", 28 | "\n", 29 | "\n", 30 | "# Script for a single bias sweep WORKS\n", 31 | "def sweep_bias_shape(wvl_start=1540, wvl_stop=1560, wvl_step=0.001, DCsource=source, bias_min=0, bias_max=0.5, bias_points=2, \n", 32 | " dirname = \"\\\\datatest\\\\\"):\n", 33 | " \"\"\"\"\"\"\n", 34 | " \n", 35 | " # Location to save the data\n", 36 | " data_dir = os.getcwd() + dirname\n", 37 | "\n", 38 | " # Initialize the DC source\n", 39 | " DCsource.connect()\n", 40 | " #DCsource.set_range_high()\n", 41 | " \n", 42 | " # Initialize the laser, connect it and set the sweep params\n", 43 | " hp = hp816x_instr.hp816x()\n", 44 | " hp.connect('GPIB0::20::INSTR')\n", 45 | " hp.sweepUnit = 'dBm'\n", 46 | " hp.sweepLaserOutput = 'lowsse' # lowsse ou highpower\n", 47 | " hp.sweepStartWvl = wvl_start * 1e-9\n", 48 | " hp.sweepStopWvl = wvl_stop * 1e-9\n", 49 | " hp.sweepStepWvl = wvl_step * 1e-9\n", 50 | " \n", 51 | " # Sweep the bias\n", 52 | " bias_testpoints = np.linspace(bias_min,bias_max,bias_points).tolist()\n", 53 | " for k in bias_testpoints: # For each bias value\n", 54 | " DCsource.source_voltage(k)\n", 55 | " #time.sleep(0.1)\n", 56 | " \n", 57 | " \n", 58 | " filename = \"V=\" + '{:.3f}'.format(k).replace(\".\",\"_\") + \".txt\"\n", 59 | " \n", 60 | " # Perform the sweep\n", 61 | " wvl_sweep,pow_sweep = hp.sweep()\n", 62 | " \n", 63 | " # Plot the results\n", 64 | " plot_sweep=False\n", 65 | " if plot_sweep == True:\n", 66 | " \n", 67 | " plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0], label='Detector1')\n", 68 | " plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[1], label='Detector2')\n", 69 | " plt.xlabel('Wavelength (nm)')\n", 70 | " plt.ylabel('Power (dBm)')\n", 71 | " plt.legend()\n", 72 | " plt.show()\n", 73 | " \n", 74 | " # Save the results\n", 75 | " if not(filename==\"\"):\n", 76 | " np.savetxt(data_dir + filename, (wvl_sweep,pow_sweep.transpose()[0],pow_sweep.transpose()[1]))\n", 77 | " print(\"Saving file : \" + filename)\n", 78 | " # Turn off the laser\n", 79 | " hp.setTLSOutput('lowsse', slot=0)\n", 80 | " hp.setTLSState('off' , slot=0)\n", 81 | " hp.setPWMPowerUnit(2, 0, 'dBm')\n", 82 | " hp.setPWMPowerUnit(2, 1, 'dBm')\n", 83 | " hp.setPWMPowerRange(2, 0, rangeMode='auto')\n", 84 | " hp.setPWMPowerRange(2, 1, rangeMode='auto')\n", 85 | " \n", 86 | " # Turn DC source Off\n", 87 | " DCsource.output_off()\n", 88 | " hp.disconnect()" 89 | ] 90 | } 91 | ], 92 | "metadata": { 93 | "kernelspec": { 94 | "display_name": "Python 2", 95 | "language": "python", 96 | "name": "python2" 97 | }, 98 | "language_info": { 99 | "codemirror_mode": { 100 | "name": "ipython", 101 | "version": 2 102 | }, 103 | "file_extension": ".py", 104 | "mimetype": "text/x-python", 105 | "name": "python", 106 | "nbconvert_exporter": "python", 107 | "pygments_lexer": "ipython2", 108 | "version": "2.7.11" 109 | } 110 | }, 111 | "nbformat": 4, 112 | "nbformat_minor": 0 113 | } 114 | -------------------------------------------------------------------------------- /dev/MRF_FBL/Algo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various resonance wavelength alignment algorithms that can be used along with the 3 | MRF object to tune/align resonant systems. 4 | 5 | Author : Simon Bélanger-de Villers (simon.belanger-de-villers.1@ulaval.ca) 6 | Created : October 2018 7 | Last edited : July 30th 2019 8 | """ 9 | 10 | # Import the required modules 11 | import time, pickle 12 | import numpy as np 13 | from scipy.optimize import minimize 14 | import matplotlib.pyplot as plt 15 | 16 | # Coordinates descent Algorithm 17 | def sweep_bias(MRF, channel, voltageList=np.linspace(0,3,100)): 18 | """ Coarse tuning of a MRF object using the coordinates descent algorithm. 19 | Might be obsolete!! 20 | """ 21 | 22 | # Turn on the laser and set the wavelength 23 | MRF.LMS.setTLSWavelength(1550e-9) 24 | MRF.LMS.setTLSState('on') 25 | 26 | power_list = [] 27 | for voltage in voltageList.tolist(): # For each bias value 28 | MRF.apply_bias(channel, voltage) 29 | time.sleep(MRF.thermalDelay) 30 | powerList.append(MRF.measurePower()) 31 | plotsweep(voltageList, powerList) 32 | MRF.apply_bias(channel, bias_testpoints[power_list.index(min(power_list))]) 33 | # Turn on the laser 34 | MRF.LMS.setTLSState('off') 35 | 36 | # Coordinates descent Algorithm 37 | def CoordsDescent(MRF, numIter, mode='manual', plotPowMat=True): 38 | """ Coarse tuning of a MRF object using the coordinates descent algorithm. 39 | Args: 40 | MRF : Microring filter object. [mrf] 41 | numIter : Number of iterations to perform. [int] 42 | Returns: 43 | x_i : Command log (applied bias to the heaters). [list] 44 | f_i : Output log (power measured a the PD). [list] 45 | """ 46 | 47 | x_i, f_i = [], [] # Bias applied , Max power measured 48 | power_mat = [[] for ii in range(MRF.num_parameters)] # Power map for each tuner 49 | 50 | # Turn on the laser and set the wavelength 51 | MRF.LMS.setTLSState('on'), time.sleep(5) 52 | for i in range(1, number_iter+1): # For each iteration 53 | print("Progress update : Iteration #{}/{} - Initiating ... ".format(i, number_iter)) 54 | for j in range(0, MRF.num_parameters): # For each ring 55 | print("Progress update : Iteration #{}/{} - Sweeping ring #{}/{} ...".format(i, number_iter+1, j+1, MRF.num_parameters)) 56 | 57 | power_list = [] 58 | for k in np.arange(MRF.LowerLimitDC[j], MRF.UpperLimitDC[j]+MRF.ResolutionDC[j], MRF.ResolutionDC[j]).tolist(): # For each bias value 59 | MRF.apply_bias(j+1, k) 60 | time.sleep(MRF.thermalDelay) 61 | power_list.append(MRF.measurePower()) 62 | power_mat[j].append(power_list) 63 | 64 | MRF.apply_bias(j+1, selectBiasPoint(bias_testpoints, power_list, mode)) 65 | 66 | x_i.append(MRF.applied_bias), f_i.append(max(power_list)) # Save the iteration Log 67 | 68 | MRF.LMS.setTLSState('off') 69 | 70 | # Plot power map 71 | if plotPowMat == True: 72 | plotPowMap(MRF, power_mat) 73 | pickle.dump( power_mat, open( MRF.data_dir + "power_mat.p", "wb" ) ) 74 | 75 | return x_i, f_i 76 | 77 | # Nelder Mead simplex algorithm 78 | def NelderMead(MRF, x0=[], port='max'): 79 | """Fine tuning of a MRF object using the Nelder Mead simplex algorithm""" 80 | 81 | # Turn on the source 82 | MRF.LMS.setTLSState('on') 83 | # Initial guess 84 | if x0 == []: 85 | x0 = MRF.applied_bias 86 | 87 | # Optimization 88 | if port == 'max': 89 | res = minimize(MRF.Drop_function, x0, method='Nelder-Mead', options={'disp': True, 'xtol': 0.0001, 'maxiter': 100, 'maxfev': 100}) 90 | elif port == 'min': 91 | res = minimize(MRF.Thru_function, x0, method='Nelder-Mead', options={'disp': True, 'xtol': 0.0001, 'maxiter': 100, 'maxfev': 100}) 92 | 93 | #Turn off the source 94 | MRF.LMS.setTLSState('off') 95 | return res.x 96 | 97 | def tuneMRF(MRF, wavelength, delay=0.1, port='max'): 98 | """Tune/stabilise the MRF object using coarse + fine algorithms""" 99 | 100 | MRF.LMS.setTLSWavelength(wavelength) 101 | 102 | CoordsDescent(MRF, 2, delay, port) # Coordinates descent 103 | NelderMead(MRF,[], port) # Nelder Mead 104 | 105 | # Various Plotting methods 106 | def plotsweep(bias, power): 107 | plt.plot(bias, power) 108 | plt.plot([bias[power.index(min(power))]], [min(power)], marker='o', markersize=10, color="red") 109 | plt.plot([bias[power.index(max(power))]], [max(power)], marker='o', markersize=10, color="red") 110 | plt.xlabel("Bias [V]") 111 | plt.ylabel("Power [dBm]") 112 | plt.show() 113 | 114 | def plotsweep2(ax, bias, power): 115 | ax.plot(bias, power) 116 | ax.plot([bias[power.index(min(power))]], [min(power)], marker='o', markersize=10, color="red") 117 | ax.plot([bias[power.index(max(power))]], [max(power)], marker='o', markersize=10, color="red") 118 | ax.xlabel("Bias [V]") 119 | ax.ylabel("Power [dBm]") 120 | return ax 121 | 122 | def plotconvergence(f_i): 123 | plt.plot(range(1,len(f_i)+1), f_i, marker='o', markersize=10, color="black") 124 | plt.xlabel("Number of iterations") 125 | plt.ylabel("Power [dBm]") 126 | plt.show() 127 | 128 | def plotPowMap(MRF, power_mat): 129 | for i in range(0,len(power_mat)): # For each tuner 130 | bias_testpoints = np.arange(MRF.LowerLimitDC[i],MRF.UpperLimitDC[i]+MRF.ResolutionDC[i],MRF.ResolutionDC[i]).tolist() 131 | for j in range(0,len(power_mat[i])): # For each iteration 132 | legend_label = "Iteration " + str(j+1) 133 | plt.plot(bias_testpoints,power_mat[i][j],label=legend_label) 134 | plt.xlabel("Bias [V]") 135 | plt.ylabel("Power [dBm]") 136 | plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 137 | plt.show() 138 | 139 | def selectBiasPoint(biasList, powerList, mode): 140 | """ Return the selected bias value that will be applied to the heaters from a data list consisting 141 | if a pair bias/power. 142 | 143 | mode : 144 | min : Always choose the bias value that minimizes the measured power. 145 | max : Always choose the bias value that maximizes the measured power. 146 | manual : Plots the measured power vs applied bias and waits for an user input of the bias to apply for every iteration. 147 | """ 148 | 149 | # Depending on the chosen mode 150 | if mode == 'manual': # Shows the power vs bias plot 151 | plotsweep(biasList, powerList) 152 | userInput = input('Enter your bias:') 153 | 154 | if userInput == "min": # Easy command to get the minimal value 155 | selectedBias = biasList[powerList.index(min(powerList))] 156 | 157 | elif userInput == "max": # Easy command to get the maximal value 158 | selectedBias = biasList[powerList.index(max(powerList))] 159 | 160 | else: # Custom value 161 | selectedBias = float(userInput) 162 | 163 | elif mode == 'min': # Always choose the minimum value 164 | selectedBias = biasList[powerList.index(min(powerList))] 165 | 166 | elif mode == 'max': # Always choose the maximum value 167 | selectedBias = biasList[powerList.index(max(powerList))] 168 | 169 | else: 170 | print('This option is not valid!') 171 | 172 | return selectedBias 173 | -------------------------------------------------------------------------------- /dev/MRF_FBL/MRF.py: -------------------------------------------------------------------------------- 1 | """ 2 | MRF Object Class and subclasses 3 | 4 | The MRF is an object controlling the microring filter. It handles input signal from a 5 | photodetector and returns inputs to the actuators (thermal phase shifters). 6 | 7 | Author : Simon Bélanger-de Villers (simon.belanger-de-villers.1@ulaval.ca) 8 | Created : October 2018 9 | Last edited : December 10th 2019 10 | """ 11 | 12 | import time, os, sys 13 | import numpy as np 14 | import matplotlib.pyplot as plt 15 | sys.path.append(os.getcwd() + '\\..\\..\\') 16 | from data.wvlsweep import wvlsweep 17 | 18 | # MRF object class (Abstract form) 19 | class RealMRF(object): 20 | 21 | thermalDelay = 0.3 # Delay between the moment the bias is applied and the time the optical power is measured 22 | _activePort = 'Drop' # The active port is the port for which the measured optical power will be returned when calling measurePower() 23 | _PWMaveragingTime = 0.02 # Averaging time for the photodetectors 24 | 25 | def __init__(self, instruments, ResolutionDC=None, dataDirectory=os.getcwd()): 26 | 27 | # Set the Parameters 28 | self.dropChan = instruments['dropChan'] # (slot, channel) tuple for the drop port of the filter. 29 | self.thruChan = instruments['thruChan'] # (slot, channel) tuple for the through port of the filter. 30 | self.LMS = instruments['LMS'] # Lightwave Measurement System (LMS) object [hp816x]. 31 | self.DCsources = instruments['DCsources'] # DC electrical power supplies objects. 32 | self.LowerLimitDC = instruments['LowerLimitDC'] 33 | self.UpperLimitDC = instruments['UpperLimitDC'] 34 | self.numParameters = len(self.DCsources) # Number of actuators in the system. 35 | if ResolutionDC == None: 36 | self.ResolutionDC = self.getDCresolution(instruments) 37 | else: 38 | self.ResolutionDC = [ResolutionDC] * self.numParameters 39 | self.applied_bias = [0.] * self.numParameters 40 | self.data_dir = dataDirectory 41 | print('Data directory is set to {}'.format(self.data_dir)) 42 | 43 | self.connectInstruments() 44 | self.DC_on() 45 | 46 | def __del__(self): 47 | """ Finalizer for the class. Executes when object is cleared from memory. """ 48 | self.DC_off() 49 | 50 | # Properties 51 | @property 52 | def laserWavelength(self): 53 | return self.LMS.getTLSWavelength() 54 | @laserWavelength.setter 55 | def laserWavelength(self, wavelength): 56 | self.LMS.setTLSWavelength(wavelength) 57 | 58 | @property 59 | def PWMaveragingTime(self): 60 | return self._PWMaveragingTime 61 | @PWMaveragingTime.setter 62 | def PWMaveragingTime(self, avgTime): 63 | self.LMS.setPWMAveragingTime(self.dropChan[0], self.dropChan[1], avgTime) 64 | self.LMS.setPWMAveragingTime(self.thruChan[0], self.thruChan[1], avgTime) 65 | self._PWMaveragingTime = avgTime 66 | 67 | @property 68 | def activePort(self): 69 | return self._activePort 70 | @activePort.setter 71 | def activePort(self, port): 72 | if port in ['Drop', 'Thru']: 73 | self._activePort = port 74 | else: 75 | print('Error! Valid pots are Drop and Thru') 76 | 77 | # methods 78 | def measurePower(self): 79 | """ Read the power measured on the PD at the Drop/Through port. """ 80 | if self._activePort == 'Drop': 81 | return self.LMS.readPWM(self.dropChan[0], self.dropChan[1]) 82 | elif self._activePort == 'Thru': 83 | return self.LMS.readPWM(self.thruChan[0], self.thruChan[1]) 84 | 85 | def connectInstruments(self): 86 | """ Connect the DC sources remotely and set their range to high. """ 87 | # Connect the power sensor 88 | self.LMS.connect('GPIB0::20::INSTR') 89 | self.resetLMS() 90 | # Connect the DC sources 91 | for instrument in self.DCsources: 92 | instrument.connect() 93 | 94 | def resetLMS(self): 95 | """ Reset the Lightwave Measurement System to it's default parameters. """ 96 | self.LMS.setTLSOutput('lowsse') 97 | self.LMS.setPWMPowerUnit(self.dropChan[0], self.dropChan[1], 'dBm') 98 | self.LMS.setPWMPowerUnit(self.thruChan[0], self.thruChan[1], 'dBm') 99 | self.LMS.setPWMPowerRange(self.dropChan[0], self.dropChan[1], rangeMode='auto') 100 | self.LMS.setPWMPowerRange(self.thruChan[0], self.thruChan[1], rangeMode='auto') 101 | 102 | def wavelengthSweep(self, sweepPower=0, wvlStart=1540e-9, wvlStop=1570e-9, wvlStep=0.02e-9, plot_det1 = True, plot_det2 = True, filename=None): 103 | """ Perform a wavelength sweep using the agilent LMS.""" 104 | wvlsweep(self.LMS, self.data_dir, sweepPower, wvlStart, wvlStop, wvlStep, plot_det1, plot_det2, filename) 105 | self.resetLMS() 106 | 107 | def apply_bias(self, source_num, bias): 108 | """ Set the bias for the ring #[ring_number] at [bias_value]. """ 109 | 110 | # Clamp the supplied bias value between 0 and the limit of the corresponding DC source 111 | limited_bias = self.limit_voltage(bias, self.LowerLimitDC[source_num-1], self.UpperLimitDC[source_num-1]) 112 | 113 | # Apply the limited_bias value to the corresponding DC_source 114 | self.applied_bias[source_num-1] = limited_bias 115 | self.DCsources[source_num-1].source_voltage(limited_bias) 116 | 117 | def apply_bias_mult(self, bias_list): 118 | """ Set the bias for each ring with values in a list of bias. """ 119 | for i in range(self.numParameters): 120 | self.apply_bias(i+1,bias_list[i]) 121 | 122 | def DC_on(self): 123 | """ Turn on the output for all the power supplies and set the voltage to 0 V. """ 124 | for powerSupply in self.DCsources: 125 | powerSupply.source_voltage(0) 126 | powerSupply.output_on() 127 | print('All DC power supplies have been turned on.') 128 | 129 | def DC_off(self): 130 | """ Turn off the DC bias for all DC sources. """ 131 | for powerSupply in self.DCsources: 132 | powerSupply.source_voltage(0) 133 | powerSupply.output_off() 134 | print('All DC power supplies have been turned off.') 135 | 136 | def objectiveFunction(self, bias_list): 137 | """ Microring Filter objective function that has to be optimized. 138 | This function accepts a 1D array of shape self.numParameters corresponding to the different bias values applied to the filter. 139 | It returns a scalar value corresponding to the measured power.""" 140 | 141 | self.apply_bias_mult(bias_list) # Apply corresponding bias to each DC source 142 | time.sleep(self.thermalDelay) # Wait for the thermal steady state 143 | return float(self.measurePower()) # Measure and return the optical power on the sensor 144 | 145 | def Drop_function(self, biasList): 146 | """ When tracking the drop port, inverse of the drop port power is used to minimze. """ 147 | return -self.objectiveFunction(biasList) 148 | 149 | def Thru_function(self, biasList): 150 | """ When tracking the Thru port, the Thru port power is used to minimze. """ 151 | return self.objectiveFunction(biasList) 152 | 153 | def get_bias(self): 154 | """ Return the bias applied with the number of significative digits. """ 155 | 156 | sig_figure = [] 157 | for res_element in self.ResolutionDC: 158 | sig_figure.append(len(str(res_element))-2) 159 | print(sig_figure) 160 | 161 | @staticmethod 162 | def getDCresolution(instruments): 163 | resolution = [] 164 | for DC in instruments['DCsources']: 165 | resolution.append(DC.resolution) 166 | return resolution 167 | 168 | @staticmethod 169 | def limit_voltage(bias, limit_inf, limit_sup): 170 | """ Returns a bias value between 0 and limit. """ 171 | if bias < limit_inf: 172 | print("Warning: The supplied bias value should be larger than " + str(limit_inf) + " V.") 173 | elif bias > limit_sup: 174 | print("Warning: The supplied bias is larger than the limit fixed at " + str(limit_sup) + " V.") 175 | return max(min(bias, limit_sup), limit_inf) -------------------------------------------------------------------------------- /dev/MRF_FBL/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/dev/MRF_FBL/__init__.py -------------------------------------------------------------------------------- /dev/MRF_FBL/jupyter/Console.py: -------------------------------------------------------------------------------- 1 | #%% Change working directory from the workspace root to the ipynb file location. Turn this addition off with the DataScience.changeDirOnImportExport setting 2 | # ms-python.python added 3 | import os 4 | try: 5 | os.chdir(os.path.join(os.getcwd(), 'dev/MRF_FBL')) 6 | print(os.getcwd()) 7 | except: 8 | pass 9 | #%% [markdown] 10 | # ## Optimisation code 11 | # 12 | # This script generates a microring filter (MRF) object made of a PD and 5 DC Sources. This device is then manipulated by the Coordinates Descent Algorithm to tune it. 13 | 14 | #%% 15 | # Load Modules 16 | import sys, os 17 | sys.path.append(os.path.join(sys.path[0],'bar','sub','dir')) 18 | from Instruments import * 19 | from methods.Algo import * 20 | from methods.MRF import RealMRF 21 | 22 | get_ipython().run_line_magic('matplotlib', 'inline') 23 | 24 | # Lightwave Measurement System 25 | LMS = hp816x_instr.hp816x() 26 | 27 | # DC Sources 28 | V1 = Agilent_E3646A(6,1) 29 | V2 = Keithley_2612B(26,'a') 30 | V3 = Keithley_2612B(26,'b') 31 | V4 = Keithley_2612B(25,'a') 32 | V5 = Keithley_2612B(25,'b') 33 | 34 | # Directory to store data 35 | data_dir = os.getcwd() + "\\datatest\\V3_part2\\" 36 | 37 | # Create the MRF object an|d connect to instruments 38 | instruments = {'LMS': LMS, 39 | 'DCsources': [V1, V2, V3, V4, V5], 40 | 'LowerLimitDC': [0]*5, 41 | 'UpperLimitDC': [3]*5} 42 | mrf = RealMRF(instruments, 2,0.0001, data_dir) 43 | mrf.connect_instruments() 44 | mrf.DC_off() 45 | 46 | #%% [markdown] 47 | # # Test the averaging of the power 48 | 49 | #%% 50 | mrf.wvl_sweep(1530, 1560, 0.05) 51 | 52 | 53 | #%% 54 | mrf.LMS.setTLSWavelength(1538*1e-9) 55 | x_i, f_i = CoordsDescent(mrf, 5, delay=0., mode='max', plotPowMat=True) # mode : manual, max, min 56 | plotconvergence(f_i) 57 | mrf.wvl_sweep(1530, 1560, 0.05) 58 | print(mrf.applied_bias) 59 | 60 | 61 | #%% 62 | print(mrf.applied_bias) 63 | 64 | #%% [markdown] 65 | # Set the bias for each heater and then peform a wavelength sweep. 66 | 67 | #%% 68 | mrf.apply_bias_mult([2.01, 2.3686, 1.67219, 2.3907, 2.2592]) 69 | mrf.wvl_sweep(1530, 1560, 0.05) 70 | 71 | 72 | #%% 73 | mrf.LMS.setTLSWavelength(1538*1e-9) 74 | mrf.average_power(2.) 75 | NelderMead(mrf, [1.24, 1.3594, 1.13407, 1.24164, 1.22108], 'max') 76 | mrf.wvl_sweep(1530, 1560, 0.02) 77 | 78 | 79 | #%% 80 | print(mrf.applied_bias) 81 | 82 | #%% [markdown] 83 | # ## Sweep Voltage and perform a wavelength sweep 84 | # 85 | # This script is used to perform a bias sweep. For every bias point, a wavelength sweep is performed. 86 | 87 | #%% 88 | import os 89 | import numpy as np 90 | import matplotlib.pyplot as plt 91 | from matplotlib import cm 92 | 93 | 94 | # Script for a single bias sweep WORKS 95 | def sweep_bias_shape(wvl_start=1540, wvl_stop=1560, wvl_step=0.001, DCsource=None, bias_min=0, bias_max=0.5, bias_points=2, 96 | dirname = "\\datatest\\"): 97 | """""" 98 | 99 | # Location to save the data 100 | data_dir = os.getcwd() + dirname 101 | 102 | # Table of content with file names 103 | toc = open(data_dir + "toc_data.txt","w+") 104 | 105 | # Initialize the DC source 106 | DCsource.connect() 107 | #DCsource.set_range_high() 108 | 109 | # Initialize the laser, connect it and set the sweep params 110 | hp = hp816x_instr.hp816x() 111 | hp.connect('GPIB0::20::INSTR') 112 | hp.sweepUnit = 'dBm' 113 | hp.sweepLaserOutput = 'lowsse' # lowsse ou highpower 114 | hp.sweepStartWvl = wvl_start * 1e-9 115 | hp.sweepStopWvl = wvl_stop * 1e-9 116 | hp.sweepStepWvl = wvl_step * 1e-9 117 | 118 | # Sweep the bias 119 | bias_testpoints = np.linspace(bias_min,bias_max,bias_points).tolist() 120 | for k in bias_testpoints: # For each bias value 121 | 122 | DCsource.source_voltage(k) 123 | #time.sleep(0.1) 124 | 125 | # Set the filename and add it to thew table of contents 126 | filename = "V=" + '{:.3f}'.format(k).replace(".","_") + ".txt" 127 | toc.write(filename+'\n') # Could make sure adding an existing file is not possible 128 | 129 | # Perform the sweep 130 | wvl_sweep,pow_sweep = hp.sweep() 131 | 132 | # Plot the results 133 | plot_sweep=False 134 | if plot_sweep == True: 135 | 136 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0], label='Detector1') 137 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[1], label='Detector2') 138 | plt.xlabel('Wavelength (nm)') 139 | plt.ylabel('Power (dBm)') 140 | plt.legend() 141 | plt.show() 142 | 143 | # Save the results 144 | if not(filename==""): 145 | np.savetxt(data_dir + filename, (wvl_sweep,pow_sweep.transpose()[0],pow_sweep.transpose()[1])) 146 | print("Saving file : " + filename) 147 | # Turn off the laser 148 | hp.setTLSOutput('lowsse', slot=0) 149 | hp.setTLSState('off' , slot=0) 150 | hp.setPWMPowerUnit(2, 0, 'dBm') 151 | hp.setPWMPowerUnit(2, 1, 'dBm') 152 | hp.setPWMPowerRange(2, 0, rangeMode='auto') 153 | hp.setPWMPowerRange(2, 1, rangeMode='auto') 154 | 155 | # Turn DC source Off 156 | DCsource.output_off() 157 | hp.disconnect() 158 | 159 | # Close the table of content file 160 | toc.close() 161 | 162 | def load_and_plot(filename,color): 163 | """ 164 | Load the wavelength sweep data in a specific file. 165 | """ 166 | 167 | A,B,C = np.loadtxt(filename, dtype=float) 168 | 169 | plt.plot(A*1e9,B, label='Detector1',color=color) 170 | plt.plot(A*1e9,C, label='Detector2',color=color) 171 | plt.xlabel('Wavelength (nm)') 172 | plt.ylabel('Power (dBm)') 173 | 174 | def multiplot(directory): 175 | """ 176 | Sweep the table of content stored along the data and plot all the bias sweep data.. 177 | """ 178 | 179 | data_dir = os.getcwd() + directory 180 | 181 | # Open the table of content 182 | toc = open(data_dir + "toc_data.txt") 183 | datafile_list = toc.read().split("\n")[:-1] 184 | 185 | cmap = cm.get_cmap('jet') 186 | compt = 0. 187 | for datafile in datafile_list: # For each bias value 188 | load_and_plot(data_dir + datafile,cmap(compt/(len(datafile_list)-1))) 189 | compt += 1 190 | ax = plt.gca() 191 | ax.get_xaxis().get_major_formatter().set_useOffset(False) 192 | plt.show() 193 | 194 | # Close the table of content 195 | toc.close() 196 | 197 | 198 | #%% 199 | from Instruments import * 200 | 201 | sweep_bias_shape(wvl_start=1515, wvl_stop=1560, wvl_step=0.05, DCsource=V1, bias_min=0, bias_max=2, bias_points=2, 202 | dirname = "\\datatest\\V1\\V1") 203 | 204 | #source.output_off() 205 | 206 | multiplot("\\datatest\\V1\\V1") 207 | 208 | 209 | #%% 210 | multiplot("\\datatest\\") 211 | 212 | #%% [markdown] 213 | # ## Tune the filter to specific central wavelengths, measure the transmission spectrum for each central wavelength 214 | 215 | #%% 216 | import os 217 | import numpy as np 218 | import matplotlib.pyplot as plt 219 | from matplotlib import cm 220 | 221 | 222 | # Script for a single bias sweep WORKS 223 | def sweep_central_wavelength(MRF, sweep_params=[1540,1570,0.02], filter_wavelengths=[1555,1565,1], dirname = "\\datatest\\"): 224 | """ 225 | 226 | Args: 227 | MRF (MRF): Microring Filter Object. 228 | sweep_params (list): 3x1 List containing sweep parameters e.g. [wvl_start, wvl_stop, wvl_step]. 229 | filter_wavelengths (list): 3x1 List containing tuning parameters e.g. [wvl_start, wvl_stop, N points] 230 | 231 | Returns: 232 | 233 | """ 234 | 235 | # Location to save the data 236 | data_dir = os.getcwd() + dirname 237 | 238 | # Table of content with file names 239 | toc = open(data_dir + "toc_data.txt","w+") 240 | 241 | # Table of content with file names 242 | lut = open(data_dir + "lut_data.txt","w+") 243 | lut.write('wvl/V1/V2/V3/V4/V5\n') 244 | 245 | # Sweep the central wavelength 246 | tuning_testpoints = np.linspace(filter_wavelengths[0],filter_wavelengths[1],filter_wavelengths[2]).tolist() 247 | for k in tuning_testpoints: # For each bias value 248 | 249 | # Tune the MRF object to the target central wavelength 250 | #tuning = tuneMRF(mrf, k, 0.1, 'Drop') 251 | tuneMRF(mrf, k, 0.1, 'Drop') 252 | 253 | # Set the filename and add it to thew table of contents 254 | filename = "c_wvl=" + '{:.3f}'.format(k).replace(".","_") + ".txt" 255 | toc.write(filename+'\n') # Could make sure adding an existing file is not possible 256 | 257 | # Store the tuning to a lookup table 258 | #lut.write('{:.3f}/{:.3f}/{:.3f}/{:.3f}/{:.3f}/{:.3f}\n'.format(k,*tuning)) 259 | 260 | # Perform the sweep 261 | MRF.wvl_sweep(sweep_params[0], sweep_params[1], sweep_params[2]) 262 | 263 | # Close the table of content and the lookup table 264 | toc.close() 265 | lut.close() 266 | 267 | 268 | #%% 269 | sweep_central_wavelength(mrf, sweep_params=[1532,1555,0.05], filter_wavelengths=[1535,1540,3], dirname = "\\datatest\\") 270 | 271 | 272 | -------------------------------------------------------------------------------- /dev/MRF_FBL/jupyter/full_analysis.py: -------------------------------------------------------------------------------- 1 | from Instruments import * 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import scipy.signal as sig 5 | from matplotlib import cm 6 | import os, time 7 | 8 | def params2filename(p1): 9 | """ This script will sweep through all bias voltages and take a wavelength sweep for each position. 10 | It will then be possible to get the best response. """ 11 | 12 | # Sweep Parameters 13 | wvl_start, wvl_stop, wvl_step = 1540e-9, 1550e-9, 0.002e-9 14 | bias_min, bias_max, bias_points = [0,0,0], [10,16,10], 12 15 | plot_sweep = False 16 | 17 | # Location to save the data 18 | data_dir = os.getcwd() + "\\data\\" 19 | 20 | # Initialize the DC sources 21 | DC_source1 = Keithley_2612B('a') 22 | DC_source2 = Keithley_2612B('b') 23 | DC_source3 = Agilent_E3646A('2') 24 | DC_source1.connect() 25 | DC_source2.connect() 26 | DC_source3.connect() 27 | DC_source1.set_range_high() 28 | DC_source2.set_range_high() 29 | DC_source3.set_range_high() 30 | 31 | # Initialize the laser, connect it and set the sweep params 32 | hp = hp816x_instr.hp816x() 33 | hp.connect('GPIB0::20::INSTR') 34 | hp.sweepUnit = 'dBm' 35 | hp.sweepLaserOutput = 'lowsse' # lowsse ou highpower 36 | hp.sweepStartWvl = wvl_start 37 | hp.sweepStopWvl = wvl_stop 38 | hp.sweepStepWvl = wvl_step 39 | 40 | for v1 in np.linspace(bias_min[0],bias_max[0],bias_points).tolist(): 41 | DC_source1.source_voltage(v1) 42 | for v2 in np.linspace(bias_min[1],bias_max[1],bias_points).tolist(): 43 | DC_source2.source_voltage(v2) 44 | for v3 in np.linspace(bias_min[0],bias_max[0],bias_points).tolist(): 45 | DC_source3.source_voltage(v3) 46 | 47 | filename = "V1=" + '{:.3f}'.format(v1).replace(".","_") + "," + "V2=" + '{:.3f}'.format(v2).replace(".","_") + "," + "V3=" + '{:.3f}'.format(v3).replace(".","_") +".txt" 48 | 49 | # Perform the wavelength sweep (and time it) 50 | start = time.time() 51 | wvl_sweep,pow_sweep = hp.sweep() 52 | print("sweep time = " + str(time.time() - start)) 53 | 54 | # Plot the results 55 | plot_sweep=False 56 | if plot_sweep == True: 57 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0], label='Detector1') 58 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[1], label='Detector2') 59 | plt.xlabel('Wavelength (nm)') 60 | plt.ylabel('Power (dBm)') 61 | plt.legend() 62 | plt.show() 63 | 64 | # Save the results 65 | if not(filename==""): 66 | np.savetxt(data_dir + filename, (wvl_sweep,pow_sweep.transpose()[0],pow_sweep.transpose()[1])) 67 | print("Saving file : " + filename) 68 | 69 | DC_source1.source_voltage(0) 70 | DC_source2.source_voltage(0) 71 | DC_source3.source_voltage(0) 72 | 73 | 74 | #%% 75 | ###### Load the sweep data save to file 76 | 77 | def load_and_plot(filename,color): 78 | 79 | A,B,C = np.loadtxt(filename, dtype=float) 80 | 81 | plt.plot(A*1e9,B, label='Detector1',color=color,) 82 | plt.plot(A*1e9,C, label='Detector2',color=color) 83 | plt.xlabel('Wavelength (nm)') 84 | plt.ylabel('Power (dBm)') 85 | 86 | data_dir = os.getcwd() + "\\data\\" 87 | 88 | # Sweep the bias 89 | bias_min = [0,0,0] 90 | bias_max = [10,16,10] 91 | bias_points = [12,12,12] 92 | v1_values = np.linspace(bias_min[0],bias_max[0],bias_points[0]).tolist() 93 | v2_values = np.linspace(bias_min[1],bias_max[1],bias_points[1]).tolist() 94 | v3_values = np.linspace(bias_min[2],bias_max[2],bias_points[2]).tolist() 95 | 96 | # plots printed 97 | v1_values = v1_values[0:3] 98 | v2_values = v2_values[0:3] 99 | v3_values = v3_values[0:3] 100 | n_curves = np.size(v1_values) * np.size(v2_values) * np.size(v3_values) 101 | print(n_curves) 102 | curve_count = 1.0 103 | cmap = cm.get_cmap('jet') 104 | 105 | f = plt.figure() 106 | for v1 in v1_values: 107 | for v2 in v2_values: 108 | for v3 in v3_values: 109 | 110 | filename = "V1=" + '{:.3f}'.format(v1).replace(".","_") + "," + "V2=" + '{:.3f}'.format(v2).replace(".","_") + "," + "V3=" + '{:.3f}'.format(v3).replace(".","_") +".txt" 111 | load_and_plot(data_dir + filename,cmap(curve_count/n_curves)) 112 | curve_count = curve_count + 1.0 113 | 114 | ax = plt.gca() 115 | ax.get_xaxis().get_major_formatter().set_useOffset(False) 116 | plt.show() 117 | f.savefig(data_dir + 'fig.pdf') 118 | 119 | 120 | 121 | ###### Find peaks and further analysis 122 | 123 | data_dir = os.getcwd() + "\\data\\" 124 | 125 | 126 | 127 | # Sweep the bias 128 | bias_min = [0,0,0] 129 | bias_max = [10,16,10] 130 | bias_points = [12,12,12] 131 | v1_values = np.linspace(bias_min[0],bias_max[0],bias_points[0]).tolist() 132 | v2_values = np.linspace(bias_min[1],bias_max[1],bias_points[1]).tolist() 133 | v3_values = np.linspace(bias_min[2],bias_max[2],bias_points[2]).tolist() 134 | 135 | # plots printed 136 | v1_values = v1_values[0:12] 137 | v2_values = v2_values[0:12] 138 | v3_values = v3_values[0:12] 139 | n_sweeps = np.size(v1_values) * np.size(v2_values) * np.size(v3_values) 140 | print(n_sweeps) 141 | sweep_ind = 1 142 | namelist = [] 143 | fmerit = [] 144 | 145 | 146 | for v1 in v1_values: 147 | for v2 in v2_values: 148 | for v3 in v3_values: 149 | 150 | filename = "V1=" + '{:.3f}'.format(v1).replace(".","_") + "," + "V2=" + '{:.3f}'.format(v2).replace(".","_") + "," + "V3=" + '{:.3f}'.format(v3).replace(".","_") +".txt" 151 | A,B,C = np.loadtxt(data_dir + filename, dtype=float) 152 | peak_ind = sig.find_peaks_cwt(C, np.array([500]))[0] 153 | peak_wvl = A[peak_ind] 154 | peak_thru = B[peak_ind] 155 | peak_drop = C[peak_ind] 156 | #print(peaks) 157 | #plt.plot(A*1e9,B, label='Detector1',color=cmap(curve_count/n_curves)) 158 | #plt.plot(A*1e9,C, label='Detector2',color=cmap(curve_count/n_curves)) 159 | #plt.plot(peak_wvl*1e9, peak_drop, marker='o', markersize=10, color="k") 160 | #plt.xlabel('Wavelength (nm)') 161 | #plt.ylabel('Power (dBm)') 162 | #curve_count = curve_count + 1.0 163 | sweep_ind = sweep_ind + 1.0 164 | progress = sweep_ind/n_sweeps 165 | if int(progress*100)%5 == 0: 166 | print(int(progress*100)) 167 | 168 | namelist.append(filename) 169 | fmerit.append(peak_drop) 170 | 171 | # Save sorted data 172 | np.savetxt(data_dir + "namelist.txt", namelist, fmt="%s" ) 173 | np.savetxt(data_dir + "fmerit.txt", np.concatenate(fmerit).ravel()) 174 | 175 | 176 | #%% 177 | indices = sorted(range(len(fmerit)), key=fmerit.__getitem__) 178 | namelist_sorted = [namelist[i] for i in indices] 179 | fmerit_sorted = [fmerit[i] for i in indices] 180 | namelist_keep = namelist_sorted[-1:-100:-1] 181 | #print(namelist_keep) 182 | 183 | curve_count = 1.0 184 | cmap = cm.get_cmap('jet') 185 | f = plt.figure() 186 | for filename in namelist_keep: 187 | A,B,C = np.loadtxt(data_dir + filename, dtype=float) 188 | plt.plot(A*1e9,B, label='Detector1',color=cmap(curve_count/len(namelist_keep))) 189 | plt.plot(A*1e9,C, label='Detector2',color=cmap(curve_count/len(namelist_keep))) 190 | plt.xlabel('Wavelength (nm)') 191 | plt.ylabel('Power (dBm)') 192 | curve_count = curve_count + 1.0 193 | 194 | ax = plt.gca() 195 | ax.get_xaxis().get_major_formatter().set_useOffset(False) 196 | plt.show() 197 | 198 | 199 | #%% 200 | fmerit_sorted=np.concatenate(fmerit).ravel().tolist() 201 | fmerit_sorted.sort() 202 | 203 | plt.plot(fmerit_sorted, label='Detector1',color=cmap(curve_count/len(namelist_keep))) 204 | plt.xlabel('Wavelength (nm)') 205 | plt.ylabel('Power (dBm)') 206 | plt.show() 207 | -------------------------------------------------------------------------------- /dev/MRF_FBL/scripts/resistancetest.py: -------------------------------------------------------------------------------- 1 | """ 2 | resistancetest.py 3 | 4 | Type : Test sequence 5 | 6 | Description: This script file performs the resistance test which consists in 7 | measuring the increase in resistance due to to a resistance heating closeby. 8 | 9 | Instruments required : Requires a Qontrol system with Q8iv card. 10 | 11 | 12 | Author : Simon Bélanger-de Villers (simon.belanger-de-villers.1@ulaval.ca) 13 | Date created : October 10th 2019 14 | Last Edited : October 10th 2019 15 | """ 16 | from Instruments.qontrol import * 17 | import numpy as np 18 | 19 | # Setup Qontroller 20 | serial_port_name = "/dev/tty.usbserial-FT06QAZ5" 21 | q = qontrol.QXOutput(serial_port_name = serial_port_name, response_timeout = 0.1) 22 | 23 | # Parameters 24 | actuators = [1, 2, 3, 4, 5] # List of the SMU channels (E.g. qontrol system) 25 | numberOfActuators = len(actuators) # Number of different actuators present 26 | voltageRange = np.linspace(0, 4, 20) # Voltage points to sweep 27 | 28 | # TODO : Measure base resistance in order to find resistance offset 29 | 30 | # Sweep the different actuators 31 | for activeActuator in actuators: 32 | 33 | # Sweep the voltage range 34 | for currentVoltage in voltageRange: 35 | 36 | # Apply the voltage to the active actuator 37 | q.v[activeActuator] = currentVoltage 38 | 39 | # Measure the applied electrical power to the heater 40 | power = q.v[activeActuator] * q.i[activeActuator] 41 | 42 | # Sweep all the actuators and measure their resistance offset 43 | # TODO: passive = all except the active 44 | for passiveActuator in actuators: 45 | 46 | # Measure voltage and current 47 | # TODO : need to apply a small bias in order to measure the resistance 48 | # TODO : how to take this into account (remove this bias) 49 | measured_voltage = q.v[passiveActuator] 50 | measured_current = q.i[passiveActuator] 51 | 52 | # Compute resistance 53 | measured_resistance = measured_voltage/measured_current 54 | 55 | # Save the voltage sweep for this actuator 56 | 57 | 58 | # TODO : Check if the resistance increase for the other heaters is proportional to the applied power 59 | # TODO : Measure the crosstalk matrix in order to see the crosstalk power vs increasing distance (what is the relationship) 60 | # TODO : Find a way to compare this value to the actual crosstalk between rings vs crosstalk between heaters (pythagorean, etc.) 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /dev/MRF_FBL/scripts/sweepcentralwvl.py: -------------------------------------------------------------------------------- 1 | """ 2 | Obsolete? 3 | Seems like a copy of MRF that was not maintained. 4 | 5 | """ 6 | 7 | import time 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | import os 11 | 12 | # MRF object class 13 | class RealMRF(object): 14 | """""" 15 | # Delay between the moment the bias is applied and the time the optical power is measured 16 | thermal_delay = 0.3 17 | 18 | def __init__(self,instruments, PWMchannel, ResolutionDC=None, data_dir=os.getcwd()): 19 | self.PWMchannel = PWMchannel-1 20 | self.LMS = instruments['LMS'] 21 | self.DCsources = instruments['DCsources'] 22 | self.num_parameters = len(self.DCsources) 23 | self.LowerLimitDC = instruments['LowerLimitDC'] 24 | self.UpperLimitDC = instruments['UpperLimitDC'] 25 | if ResolutionDC == None: 26 | self.ResolutionDC = self.getDCresolution(instruments) 27 | else: 28 | self.ResolutionDC = [ResolutionDC] * self.num_parameters 29 | self.applied_bias = [0.] * self.num_parameters 30 | self.data_dir = data_dir 31 | print(self.data_dir) 32 | 33 | @staticmethod 34 | def getDCresolution(instruments): 35 | resolution = [] 36 | for DC in instruments['DCsources']: 37 | resolution.append(DC.resolution) 38 | return resolution 39 | 40 | 41 | def connect_instruments(self): 42 | """Conncet the DC sources remotely and set their range to high.""" 43 | # Connect the power sensor 44 | self.LMS.connect('GPIB0::20::INSTR') 45 | self.setup_LMS() 46 | # Connect the DC sources 47 | for instrument in self.DCsources: 48 | instrument.connect() 49 | 50 | def setup_LMS(self): 51 | """Setup the Lightwave Measurement System for use.""" 52 | self.LMS.setTLSOutput('lowsse') 53 | self.LMS.setPWMPowerUnit(2, 0, 'dBm') 54 | self.LMS.setPWMPowerUnit(2, 1, 'dBm') 55 | self.LMS.setPWMPowerRange(2, 0, rangeMode='auto') 56 | self.LMS.setPWMPowerRange(2, 1, rangeMode='auto') 57 | 58 | def wvl_sweep(self, wvl_start=1540, wvl_stop=1570, wvl_step=0.02, plot_det1 = True, plot_det2 = True, filename=None): 59 | """Perform a wavelength sweep over the specified range.""" 60 | 61 | # Init Instrument 62 | self.LMS.sweepUnit = 'dBm' 63 | self.LMS.sweepLaserOutput = 'lowsse' # lowsse ou highpower 64 | self.LMS.sweepStartWvl = wvl_start * 1e-9 65 | self.LMS.sweepStopWvl = wvl_stop * 1e-9 66 | self.LMS.sweepStepWvl = wvl_step * 1e-9 67 | self.LMS.sweepInitialRange = -20 68 | self.LMS.sweepRangeDecrement = 20 69 | self.LMS.setPWMPowerUnit(2, 0, 'dBm') 70 | self.LMS.setPWMPowerUnit(2, 1, 'dBm') 71 | 72 | #Sweep 73 | wvl_sweep,pow_sweep = self.LMS.sweep() 74 | 75 | # Turn off the laser 76 | self.setup_LMS() 77 | 78 | # Plot the results 79 | f = plt.figure() 80 | if plot_det1 == True: 81 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[0], label='Detector1') 82 | if plot_det2 == True: 83 | plt.plot(wvl_sweep*1e9,pow_sweep.transpose()[1], label='Detector2') 84 | if plot_det1 or plot_det2: 85 | plt.xlabel('Wavelength (nm)') 86 | plt.ylabel('Power (dBm)') 87 | plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 88 | plt.show() 89 | 90 | # Save the results 91 | if filename == None: 92 | filename = raw_input("Enter the name of the file:") 93 | if not(filename==""): 94 | complete_name = self.data_dir + filename + '_' + str(wvl_start).replace('.',',') +'_' + str(wvl_stop).replace('.',',') + '_' + str(wvl_step).replace('.',',') 95 | np.savetxt(complete_name + ".txt", (wvl_sweep,pow_sweep.transpose()[0],pow_sweep.transpose()[1])) 96 | f.savefig(complete_name + ".pdf") 97 | 98 | def apply_bias(self,source_num,bias): 99 | """Set the bias for the ring #[ring_number] at [bias_value]""" 100 | 101 | # Clamp the supplied bias value between 0 and the limit of the corresponding DC source 102 | limited_bias = self.limit_voltage(bias, self.LowerLimitDC[source_num-1], self.UpperLimitDC[source_num-1]) 103 | 104 | # Apply the limited_bias value to the corresponding DC_source 105 | self.applied_bias[source_num-1] = limited_bias 106 | self.DCsources[source_num-1].source_voltage(limited_bias) 107 | 108 | def apply_bias_mult(self, bias_list): 109 | """Set the bias for each ring with values in a list of bias.""" 110 | for i in range(self.num_parameters): 111 | self.apply_bias(i+1,bias_list[i]) 112 | 113 | def average_power(self, avgtime): 114 | """""" 115 | self.LMS.setPWMAveragingTime(2, self.PWMchannel, avgtime) 116 | 117 | @staticmethod 118 | def limit_voltage(bias, limit_inf, limit_sup): 119 | """ Returns a bias value between 0 and limit.""" 120 | if bias < limit_inf: 121 | print("Warning: The supplied bias value should be larger than " + str(limit_inf) + " V.") 122 | elif bias > limit_sup: 123 | print("Warning: The supplied bias is larger than the limit fixed at " + str(limit_sup) + " V.") 124 | return max(min(bias, limit_sup), limit_inf) 125 | 126 | def DC_off(self): 127 | """Turn off the DC bias for all DC sources.""" 128 | for i in range(self.num_parameters): 129 | self.apply_bias(i,0) 130 | 131 | def obj_function(self,bias_list): 132 | """ 133 | Microring Filter objective function that has to be optimized. 134 | 135 | This function accepts a 1D array of shape self.num_parameters 136 | corresponding to the different bias values applied to the filter. 137 | It returns a scalar value corresponding to the measured power. 138 | 139 | """ 140 | # Apply corresponding bias to each DC source 141 | self.apply_bias_mult(bias_list) 142 | 143 | # Wait for the thermal steady state 144 | time.sleep(self.thermal_delay) 145 | 146 | # Measure the optical power on the sensor 147 | return float(self.LMS.readPWM(2, self.PWMchannel)) 148 | 149 | def Drop_function(self,bias_list): 150 | """ 151 | When tracking the drop port, inverse of the drop port power is used to minimze. 152 | """ 153 | return -self.obj_function(bias_list) 154 | 155 | def Thru_function(self, bias_list): 156 | """ 157 | When tracking the Thru port, the Thru port power is used to minimze. 158 | """ 159 | return self.obj_function(bias_list) 160 | 161 | def get_bias(self): 162 | """Return the bias applied with the number of significative digits. """ 163 | 164 | sig_figure = [] 165 | for res_element in self.ResolutionDC: 166 | sig_figure.append(len(str(res_element))-2) 167 | print(sig_figure) -------------------------------------------------------------------------------- /dev/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/dev/__init__.py -------------------------------------------------------------------------------- /dev/autoEdge/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Main code for the auto-alignment feature for the edge coupler. 3 | 4 | Author : Simon Belanger-de Villers 5 | Created : January 6th 2020 6 | Last edited : January 6th 2020 7 | 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | import time 12 | 13 | def autoEdge(): 14 | """Feedback look for alignment of the edge coupler position.""" 15 | 16 | # Parameters 17 | numberIterations = 100 # Number of iterations that will be performed 18 | actuatorON = True # Perform control over the system with the actuator 19 | zPos = hexapod.getZpos # Initial Z position of the transposer 20 | 21 | #Execute the code while no interruption 22 | try: 23 | 24 | # Init arrays 25 | # iteration : Iteration number in the loop 26 | # power : Power measured at the PD for every iteration 27 | # zPosition : Z position of the transposer for every iteration 28 | iteration, power, zPosition = [], [], [] 29 | 30 | # Configure the plot for tracking 31 | fig, ax = plt.subplots(nrows=1, sharex=True) 32 | ax.set_title('autoEdge') 33 | ax.set_xlabel('Iteration') 34 | ax1 = ax.twinx() 35 | ax.set_ylabel('Optical Power [dB]', color='b'); ax.tick_params('y', colors='b') 36 | ax1.set_ylabel('Z position [m]', color='r'); ax1.tick_params('y', colors='r') 37 | 38 | # Turn the laser on 39 | laser.output_on() 40 | 41 | # Feedback loop 42 | for i in range(numberIterations): 43 | # time.sleep(0.2) 44 | 45 | hexapod.setPos(zPos) 46 | X = PD.measurePower() # Measure the optical power before actuating -> X 47 | 48 | if actuatorON: 49 | zPos += deltaZPos # Increase the Z value of the transposer 50 | 51 | hexapod.setPos(zPos) 52 | Y = PD.measurePower() # Measure the optical power after actuating -> Y 53 | 54 | if Y>X: 55 | deltaZPos = -1 * deltaZPos 56 | 57 | # Actuate values 58 | iteration.append(i); power.append(Y); zPosition.append(zPos) 59 | 60 | # Draw the plot 61 | ax.plot(iteration, power, 'b') # Optical Power 62 | ax1.plot(iteration, zPosition, 'r') # Z position 63 | 64 | # Set the limits for the axes 65 | ax.set_xlim([min(iteration),max(iteration)+1]) 66 | if len(iteration)>100: 67 | ax.set_xlim([max(iteration)-100,max(iteration)]) 68 | ax.set_ylim([min(power), max(power)]) 69 | ax1.set_ylim([min(zPosition), max(zPosition)]) 70 | plt.pause(0.01) 71 | 72 | #Average 73 | ax.cla(); ax1.cla() 74 | #ax.plot([min(Iteration),max(Iteration)+1], [np.average(np.asarray(V_inI)),np.average(np.asarray(V_inI))], 'b--') 75 | #ax1.plot([min(Iteration),max(Iteration)+1], [np.average(np.asarray(I_outI)),np.average(np.asarray(I_outI))], 'r--') 76 | 77 | fig.tight_layout() 78 | ax.set_xlim([min(iteration), max(iteration)]); ax1.set_xlim([min(iteration), max(iteration)]) 79 | fig.show() 80 | 81 | # Save the data as text file 82 | np.savetxt('IQ_' + time.strftime("%Y-%m-%d@%H_%M_%S") + '.txt', (iteration, power, zPosition)) 83 | 84 | # Turn the laser off 85 | laser.output_off() 86 | 87 | # To close the utilitary 88 | except KeyboardInterrupt: 89 | pass 90 | 91 | -------------------------------------------------------------------------------- /dev/laserServer/app-client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import socket 5 | import selectors 6 | import traceback 7 | 8 | import lib 9 | 10 | sel = selectors.DefaultSelector() 11 | 12 | 13 | def create_request(command, argument): 14 | return dict( 15 | type="text/json", 16 | encoding="utf-8", 17 | content=dict(action=command, value=argument), 18 | ) 19 | 20 | 21 | def start_connection(host, port, request): 22 | addr = (host, port) 23 | print("starting connection to", addr) 24 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 25 | sock.setblocking(False) 26 | sock.connect_ex(addr) 27 | events = selectors.EVENT_READ | selectors.EVENT_WRITE 28 | message = lib.clientMessage(sel, sock, addr, request) 29 | sel.register(sock, events, data=message) 30 | 31 | 32 | if len(sys.argv) != 5: 33 | print("usage:", sys.argv[0], " ") 34 | sys.exit(1) 35 | 36 | host, port = sys.argv[1], int(sys.argv[2]) 37 | command, argument = sys.argv[3], sys.argv[4] 38 | request = create_request(command, argument) 39 | start_connection(host, port, request) 40 | 41 | try: 42 | while True: 43 | events = sel.select(timeout=1) 44 | for key, mask in events: 45 | message = key.data 46 | try: 47 | message.process_events(mask) 48 | except Exception: 49 | print( 50 | "main: error: exception for", 51 | f"{message.addr}:\n{traceback.format_exc()}", 52 | ) 53 | message.close() 54 | # Check for a socket being monitored to continue. 55 | if not sel.get_map(): 56 | break 57 | except KeyboardInterrupt: 58 | print("caught keyboard interrupt, exiting") 59 | finally: 60 | sel.close() -------------------------------------------------------------------------------- /dev/laserServer/app-server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import socket 5 | import selectors 6 | import traceback 7 | 8 | import lib 9 | 10 | sel = selectors.DefaultSelector() 11 | 12 | 13 | def accept_wrapper(sock): 14 | conn, addr = sock.accept() # Should be ready to read 15 | print("accepted connection from", addr) 16 | conn.setblocking(False) 17 | message = lib.serverMessage(sel, conn, addr) 18 | sel.register(conn, selectors.EVENT_READ, data=message) 19 | 20 | 21 | if len(sys.argv) != 3: 22 | print("usage:", sys.argv[0], " ") 23 | sys.exit(1) 24 | 25 | host, port = sys.argv[1], int(sys.argv[2]) 26 | lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 27 | # Avoid bind() exception: OSError: [Errno 48] Address already in use 28 | lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 29 | lsock.bind((host, port)) 30 | lsock.listen() 31 | print("listening on", (host, port)) 32 | lsock.setblocking(False) 33 | sel.register(lsock, selectors.EVENT_READ, data=None) 34 | 35 | try: 36 | while True: 37 | events = sel.select(timeout=None) 38 | for key, mask in events: 39 | if key.data is None: 40 | accept_wrapper(key.fileobj) 41 | else: 42 | message = key.data 43 | try: 44 | message.process_events(mask) 45 | except Exception: 46 | print( 47 | "main: error: exception for", 48 | f"{message.addr}:\n{traceback.format_exc()}", 49 | ) 50 | message.close() 51 | except KeyboardInterrupt: 52 | print("caught keyboard interrupt, exiting") 53 | finally: 54 | sel.close() -------------------------------------------------------------------------------- /dev/laserServer/lib.py: -------------------------------------------------------------------------------- 1 | import sys, selectors, json, io, struct 2 | 3 | request_search = { 4 | "morpheus" : "Follow the white rabbit. \U0001f430", 5 | "ring" : "In the caves beneath the Misty Mountains. \U0001f48d", 6 | "\U0001f436": "\U0001f43e Playing ball! \U0001f3d0", 7 | 'allo' : "bonjour" 8 | } 9 | 10 | class Message(object): 11 | """ General message class. """ 12 | def __init__(self, selector, sock, addr): 13 | self.selector = selector 14 | self.sock = sock 15 | self.addr = addr 16 | self._recv_buffer = b"" 17 | self._send_buffer = b"" 18 | self._jsonheader_len = None 19 | self.jsonheader = None 20 | self.request = None 21 | 22 | def _set_selector_events_mask(self, mode): 23 | """Set selector to listen for events: mode is 'r', 'w', or 'rw'.""" 24 | if mode == "r": 25 | events = selectors.EVENT_READ 26 | elif mode == "w": 27 | events = selectors.EVENT_WRITE 28 | elif mode == "rw": 29 | events = selectors.EVENT_READ | selectors.EVENT_WRITE 30 | else: 31 | raise ValueError(f"Invalid events mask mode {repr(mode)}.") 32 | self.selector.modify(self.sock, events, data=self) 33 | 34 | def _read(self): 35 | try: 36 | # Should be ready to read 37 | data = self.sock.recv(4096) 38 | except BlockingIOError: 39 | # Resource temporarily unavailable (errno EWOULDBLOCK) 40 | pass 41 | else: 42 | if data: 43 | self._recv_buffer += data 44 | else: 45 | raise RuntimeError("Peer closed.") 46 | 47 | def _write(self): 48 | if self._send_buffer: 49 | print("sending", repr(self._send_buffer), "to", self.addr) 50 | try: 51 | # Should be ready to write 52 | sent = self.sock.send(self._send_buffer) 53 | except BlockingIOError: 54 | # Resource temporarily unavailable (errno EWOULDBLOCK) 55 | pass 56 | else: 57 | self._send_buffer = self._send_buffer[sent:] 58 | 59 | def read(self): 60 | pass 61 | 62 | def write(self): 63 | pass 64 | 65 | def _json_encode(self, obj, encoding): 66 | return json.dumps(obj, ensure_ascii=False).encode(encoding) 67 | 68 | def _json_decode(self, json_bytes, encoding): 69 | tiow = io.TextIOWrapper( 70 | io.BytesIO(json_bytes), encoding=encoding, newline="" 71 | ) 72 | obj = json.load(tiow) 73 | tiow.close() 74 | return obj 75 | 76 | def _create_message( 77 | self, *, content_bytes, content_type, content_encoding 78 | ): 79 | jsonheader = { 80 | "byteorder": sys.byteorder, 81 | "content-type": content_type, 82 | "content-encoding": content_encoding, 83 | "content-length": len(content_bytes), 84 | } 85 | jsonheader_bytes = self._json_encode(jsonheader, "utf-8") 86 | message_hdr = struct.pack(">H", len(jsonheader_bytes)) 87 | message = message_hdr + jsonheader_bytes + content_bytes 88 | return message 89 | 90 | def process_events(self, mask): 91 | if mask & selectors.EVENT_READ: 92 | self.read() 93 | if mask & selectors.EVENT_WRITE: 94 | self.write() 95 | 96 | def close(self): 97 | print("closing connection to", self.addr) 98 | try: 99 | self.selector.unregister(self.sock) 100 | except Exception as e: 101 | print( 102 | f"error: selector.unregister() exception for", 103 | f"{self.addr}: {repr(e)}", 104 | ) 105 | 106 | try: 107 | self.sock.close() 108 | except OSError as e: 109 | print( 110 | f"error: socket.close() exception for", 111 | f"{self.addr}: {repr(e)}", 112 | ) 113 | finally: 114 | # Delete reference to socket object for garbage collection 115 | self.sock = None 116 | 117 | def process_protoheader(self): 118 | hdrlen = 2 119 | if len(self._recv_buffer) >= hdrlen: 120 | self._jsonheader_len = struct.unpack( 121 | ">H", self._recv_buffer[:hdrlen] 122 | )[0] 123 | self._recv_buffer = self._recv_buffer[hdrlen:] 124 | 125 | def process_jsonheader(self): 126 | hdrlen = self._jsonheader_len 127 | if len(self._recv_buffer) >= hdrlen: 128 | self.jsonheader = self._json_decode( 129 | self._recv_buffer[:hdrlen], "utf-8" 130 | ) 131 | self._recv_buffer = self._recv_buffer[hdrlen:] 132 | for reqhdr in ( 133 | "byteorder", 134 | "content-length", 135 | "content-type", 136 | "content-encoding", 137 | ): 138 | if reqhdr not in self.jsonheader: 139 | raise ValueError(f'Missing required header "{reqhdr}".') 140 | 141 | class clientMessage(Message): 142 | """ Client message class. """ 143 | def __init__(self, selector, sock, addr, request): 144 | super().__init__(selector, sock, addr) 145 | self.request = request 146 | self._request_queued = False 147 | self.response = None 148 | 149 | def _process_response_json_content(self): 150 | content = self.response 151 | result = content.get("result") 152 | print(f"got result: {result}") 153 | 154 | def _process_response_binary_content(self): 155 | content = self.response 156 | print(f"got response: {repr(content)}") 157 | 158 | def read(self): 159 | self._read() 160 | 161 | if self._jsonheader_len is None: 162 | self.process_protoheader() 163 | 164 | if self._jsonheader_len is not None: 165 | if self.jsonheader is None: 166 | self.process_jsonheader() 167 | 168 | if self.jsonheader: 169 | if self.response is None: 170 | self.process_response() 171 | 172 | def write(self): 173 | if not self._request_queued: 174 | self.queue_request() 175 | 176 | self._write() 177 | 178 | if self._request_queued: 179 | if not self._send_buffer: 180 | # Set selector to listen for read events, we're done writing. 181 | self._set_selector_events_mask("r") 182 | 183 | def queue_request(self): 184 | content = self.request["content"] 185 | content_type = self.request["type"] 186 | content_encoding = self.request["encoding"] 187 | if content_type == "text/json": 188 | req = { 189 | "content_bytes": self._json_encode(content, content_encoding), 190 | "content_type": content_type, 191 | "content_encoding": content_encoding, 192 | } 193 | else: 194 | req = { 195 | "content_bytes": content, 196 | "content_type": content_type, 197 | "content_encoding": content_encoding, 198 | } 199 | message = self._create_message(**req) 200 | self._send_buffer += message 201 | self._request_queued = True 202 | 203 | def process_response(self): 204 | content_len = self.jsonheader["content-length"] 205 | if not len(self._recv_buffer) >= content_len: 206 | return 207 | data = self._recv_buffer[:content_len] 208 | self._recv_buffer = self._recv_buffer[content_len:] 209 | if self.jsonheader["content-type"] == "text/json": 210 | encoding = self.jsonheader["content-encoding"] 211 | self.response = self._json_decode(data, encoding) 212 | print("received response", repr(self.response), "from", self.addr) 213 | self._process_response_json_content() 214 | else: 215 | # Binary or unknown content-type 216 | self.response = data 217 | print( 218 | f'received {self.jsonheader["content-type"]} response from', 219 | self.addr, 220 | ) 221 | self._process_response_binary_content() 222 | # Close when response has been processed 223 | self.close() 224 | 225 | class serverMessage(Message): 226 | """ Server message class. """ 227 | def __init__(self, selector, sock, addr): 228 | super().__init__(selector, sock, addr) 229 | self.response_created = False 230 | 231 | def _write(self): 232 | if self._send_buffer: 233 | print("sending", repr(self._send_buffer), "to", self.addr) 234 | try: 235 | # Should be ready to write 236 | sent = self.sock.send(self._send_buffer) 237 | except BlockingIOError: 238 | # Resource temporarily unavailable (errno EWOULDBLOCK) 239 | pass 240 | else: 241 | self._send_buffer = self._send_buffer[sent:] 242 | # Close when the buffer is drained. The response has been sent. 243 | if sent and not self._send_buffer: 244 | self.close() 245 | 246 | def _create_response_json_content(self): 247 | action = self.request.get("action") 248 | if action == "search": 249 | query = self.request.get("value") 250 | answer = request_search.get(query) or f'No match for "{query}".' 251 | content = {"result": answer} 252 | if action == 'plot': 253 | query = self.request.get("value") 254 | import matplotlib.pyplot as plt 255 | plt.plot([1,2,3],[1,2,3]) 256 | plt.show() 257 | content = {"result": query} 258 | else: 259 | content = {"result": f'Error: invalid action "{action}".'} 260 | content_encoding = "utf-8" 261 | response = { 262 | "content_bytes": self._json_encode(content, content_encoding), 263 | "content_type": "text/json", 264 | "content_encoding": content_encoding, 265 | } 266 | return response 267 | 268 | def _create_response_binary_content(self): 269 | response = { 270 | "content_bytes": b"First 10 bytes of request: " 271 | + self.request[:10], 272 | "content_type": "binary/custom-server-binary-type", 273 | "content_encoding": "binary", 274 | } 275 | return response 276 | 277 | def read(self): 278 | self._read() 279 | 280 | if self._jsonheader_len is None: 281 | self.process_protoheader() 282 | 283 | if self._jsonheader_len is not None: 284 | if self.jsonheader is None: 285 | self.process_jsonheader() 286 | 287 | if self.jsonheader: 288 | if self.request is None: 289 | self.process_request() 290 | 291 | def write(self): 292 | if self.request: 293 | if not self.response_created: 294 | self.create_response() 295 | 296 | self._write() 297 | 298 | def process_request(self): 299 | content_len = self.jsonheader["content-length"] 300 | if not len(self._recv_buffer) >= content_len: 301 | return 302 | data = self._recv_buffer[:content_len] 303 | self._recv_buffer = self._recv_buffer[content_len:] 304 | if self.jsonheader["content-type"] == "text/json": 305 | encoding = self.jsonheader["content-encoding"] 306 | self.request = self._json_decode(data, encoding) 307 | print("received request", repr(self.request), "from", self.addr) 308 | else: 309 | # Binary or unknown content-type 310 | self.request = data 311 | print( 312 | f'received {self.jsonheader["content-type"]} request from', 313 | self.addr, 314 | ) 315 | # Set selector to listen for write events, we're done reading. 316 | self._set_selector_events_mask("w") 317 | 318 | def create_response(self): 319 | if self.jsonheader["content-type"] == "text/json": 320 | response = self._create_response_json_content() 321 | else: 322 | # Binary or unknown content-type 323 | response = self._create_response_binary_content() 324 | message = self._create_message(**response) 325 | self.response_created = True 326 | self._send_buffer += message -------------------------------------------------------------------------------- /dev/misc/fiberarray.py: -------------------------------------------------------------------------------- 1 | #TODO : Interaction with the lid and limitations due to it. 2 | #TODO : Manage collisions between the fiber array and the chip (max angle) 3 | 4 | 5 | import math 6 | from matplotlib import pyplot as plt 7 | from shapely.geometry.polygon import LinearRing, Polygon, LineString 8 | from shapely import affinity 9 | from matplotlib.widgets import Slider 10 | 11 | class fiber_array(object): 12 | """ Class that implements a fiber array in order to calculate the array angle required for the right incidence angle 13 | to the grating coupler. 14 | 15 | Simon Bélanger-de Villers 16 | August 2018 17 | 18 | Added graphical UI in June 2019 19 | """ 20 | 21 | n_glass = 1.4 # Index of refraction of silica glass [-] 22 | n_air = 1. # Index of refraction of air [-] 23 | 24 | 25 | array_width = 30 # Total thickness of the fiber array [µm] 26 | array_heigth = 100 # Total height of the fiber array [µm] 27 | lid_thickness = 10 # Thickness of the lid [µm] 28 | vertical_offset = 100 # Vertical offset between the corner of the top of the lid and the chip [µm] 29 | 30 | def __init__(self, name = "name", pol = 'TE', num_IO = 12, theta_offset = 330., theta_array = 10., theta_polish = 30., pitch = 127): 31 | self.name = name 32 | self.pol = pol 33 | self.num_IO = num_IO 34 | self.pitch = pitch 35 | 36 | self.theta_offset = theta_offset 37 | self.theta_polish = theta_polish # Between 0 and 45 degrees 38 | self.theta_array = theta_array 39 | 40 | self.theta_refrac = self.theta_refrac() 41 | self.theta_inc_max = self.theta_incidence_max() 42 | 43 | self.theta_incidence = self.theta_array + self.theta_refrac 44 | 45 | def theta_refrac(self): 46 | """ Compute the refraction angle out of the fiber array the given polish angle and materials. """ 47 | theta_refrac = math.degrees(math.asin((self.n_glass/self.n_air) * math.sin(math.radians(self.theta_polish)))) #- self.theta_polish 48 | return theta_refrac 49 | 50 | def measure_theta_array(self, theta_incidence): 51 | """ For a given incidence angle, compute the required array angle. """ 52 | theta_array = theta_incidence - self.theta_refrac 53 | return theta_array 54 | 55 | def theta_incidence_max(self): 56 | """ Find the maximum allowable incidence angle for a given fiber polish. 57 | Could find this geometrically using shapely also. 58 | """ 59 | theta_inc_max = self.theta_polish + self.theta_refrac 60 | return theta_inc_max 61 | 62 | def measure_theta_measured(self, theta_incidence): 63 | """ For a given incidence angle, compute the required measured angle on the setup. """ 64 | theta_measured = theta_incidence - self.theta_refrac + self.theta_offset 65 | return round(theta_measured,2) 66 | 67 | def measure_theta_incidence(self, theta_measured): 68 | """ For a given measured angle on the setup, compute the incidence angle on the chip. """ 69 | theta_incidence = theta_measured - self.theta_offset + self.theta_refrac 70 | return theta_incidence 71 | 72 | def draw_shapes(self, axes): 73 | """ Draw the fiber array and the light ray from the fiber array to the chip. """ 74 | 75 | # Build the fiber array from a rectangle with a polish angle (bevel) 76 | rect = Polygon([(0, 0), (self.array_width, 0), (self.array_width, self.array_heigth), (0, self.array_heigth)]) 77 | bevel = Polygon([(0, 0), (self.array_width, 0), (0, self.array_width*math.tan(math.radians(self.theta_polish)))]) 78 | if self.theta_polish != 0: 79 | far = rect.difference(bevel) 80 | else: 81 | far = rect 82 | far = affinity.rotate(far, self.theta_array, origin=(0, self.array_heigth)) # Rotate the array angle 83 | far = affinity.translate(far, yoff=self.vertical_offset) # Vertically offset the fiber array 84 | axes.plot(far.exterior.xy[0], far.exterior.xy[1], color="b") 85 | 86 | # Ray from the fiber 87 | ray = LineString([(self.array_width-self.lid_thickness, self.array_heigth), (self.array_width-self.lid_thickness, self.lid_thickness*math.tan(math.radians(self.theta_polish)))]) 88 | ray = affinity.rotate(ray, self.theta_array,(0, self.array_heigth)) 89 | ray = affinity.translate(ray, yoff=self.vertical_offset) 90 | axes.plot(ray.xy[0], ray.xy[1], color="r") 91 | 92 | # Ray free space 93 | ray2 = LineString([ray.coords[1], (ray.coords[1][0]+ray.coords[1][1]*math.tan(math.radians(self.theta_incidence)), 0)]) 94 | axes.plot(ray2.xy[0], ray2.xy[1], color="r") 95 | 96 | # The chip 97 | chip = Polygon([(-100, 0), (100, 0), (100, -20), (-100, -20)]) 98 | axes.plot(chip.exterior.xy[0], chip.exterior.xy[1], color="k") 99 | 100 | # Horizontal line 101 | hline = LineString([(0, self.array_heigth+self.vertical_offset), (0,-20)]) 102 | axes.plot(hline.xy[0], hline.xy[1], color="k", linestyle="dashed") 103 | 104 | def show_util(self): 105 | 106 | fig, ax = plt.subplots() 107 | plt.subplots_adjust(left=0.25, bottom=0.35) 108 | 109 | ax.axis([-200, 200, -50, 200]) 110 | 111 | ax_arrayangle = fig.add_axes([0.25, 0.25, 0.65, 0.03]) # [left, bottom, width, height] 112 | ax_polishangle = fig.add_axes([0.25, 0.15, 0.65, 0.03]) 113 | ax_verticaloff = fig.add_axes([0.25, 0.05, 0.65, 0.03]) 114 | s_arrayangle = Slider(ax_arrayangle, 'Array\n angle [°]', 0, 30, valinit=self.theta_array) 115 | s_polishangle = Slider(ax_polishangle, 'Polish\n angle [°]', 0, 45, valinit=self.theta_polish) 116 | s_verticaloff = Slider(ax_verticaloff, 'Vertical\n offset [µm]', -100, 100, valinit=self.vertical_offset) 117 | 118 | self.draw_shapes(ax) 119 | 120 | def update(val): 121 | self.theta_array = s_arrayangle.val 122 | self.theta_polish = s_polishangle.val 123 | self.vertical_offset = s_verticaloff.val 124 | self.draw_shapes(ax) 125 | del ax.lines[0:-6] 126 | fig.canvas.draw() 127 | 128 | s_arrayangle.on_changed(update) 129 | s_polishangle.on_changed(update) 130 | s_verticaloff.on_changed(update) 131 | 132 | plt.show() 133 | 134 | 135 | 136 | def __repr__(self): 137 | log = """Fiber Array object with properties: 138 | name : %s 139 | polarization : %s 140 | number of I/O : %d 141 | pitch : %d um 142 | polish angle : %2.2f deg 143 | array offset angle : %3.2f deg 144 | maximum incidence angle : %2.2f deg 145 | """ % (self.name, self.pol, self.num_IO, self.pitch, self.theta_polish, self.theta_offset, self.theta_inc_max) 146 | return log 147 | 148 | if __name__ == '__main__': 149 | 150 | far = fiber_array(name = "Aeponyx_24", theta_array = 0., theta_polish = 0.) 151 | print(far) 152 | 153 | far.show_util() 154 | -------------------------------------------------------------------------------- /dev/misc/filedialog.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | from tkinter import filedialog 3 | 4 | root = Tk() 5 | root.filename = filedialog.askopenfilename(initialdir="/", title="Select file", filetypes=[('txt', '*.txt')]) 6 | print(root.filename) -------------------------------------------------------------------------------- /dev/misc/qontrolGUI.py: -------------------------------------------------------------------------------- 1 | """ 2 | GUI for the Qontrol that allows to manually control the device instead of writing code. 3 | 4 | Author : Simon Bélanger-de Villers 5 | Created : July 31st 2019 6 | Last Edited : July 31st 2019 7 | """ 8 | import sys 9 | from PyQt5 import QtWidgets 10 | 11 | class QontrolGUI(QtWidgets.QMainWindow): 12 | 13 | def __init__(self, parent=None): 14 | super(QontrolGUI, self).__init__(parent) 15 | 16 | window = QtWidgets.QWidget() 17 | 18 | # Text label widget 19 | label = QtWidgets.QLabel('Serial address : ') 20 | 21 | # Combo box widget 22 | self.cb = QtWidgets.QComboBox() 23 | self.cb.addItems(self.listSerialPorts()) 24 | 25 | # Button widget 26 | but = QtWidgets.QPushButton('Connect') 27 | but.clicked.connect(self.selectSerialPort) 28 | 29 | # Layout containing the serial label, combobox and button 30 | serialLayout = QtWidgets.QHBoxLayout() 31 | serialLayout.addWidget(label) 32 | serialLayout.addWidget(self.cb) 33 | serialLayout.addWidget(but) 34 | print(serialLayout) 35 | 36 | window.setLayout(serialLayout) 37 | 38 | self.setCentralWidget(window) 39 | 40 | def listSerialPorts(self): 41 | " List all serial ports on this computer. " 42 | 43 | from serial.tools.list_ports import comports 44 | 45 | listPorts = [] 46 | for ports in comports(): 47 | listPorts.append(ports.device) 48 | return listPorts 49 | 50 | def selectSerialPort(self): 51 | 52 | from qontrol import QXOutput 53 | 54 | try: 55 | self.q = QXOutput(serial_port_name = str(self.cb.currentText()), response_timeout = 0.1) 56 | except RuntimeError: 57 | pass 58 | 59 | 60 | 61 | 62 | 63 | 64 | if __name__ == "__main__": 65 | import sys 66 | app = QtWidgets.QApplication(sys.argv) 67 | qg = QontrolGUI() 68 | qg.show() 69 | app.exec_() 70 | -------------------------------------------------------------------------------- /instruments/Agilent_E3631A.py: -------------------------------------------------------------------------------- 1 | from Instruments.Instrument_pyvisa import Instrument_pyvisa 2 | 3 | 4 | # DC source class 5 | class Agilent_E3631A(Instrument_pyvisa): 6 | """Creates a DC source object in order to communicate with the Agilent E3631A triple output power supply.""" 7 | 8 | def __init__(self, gpib_num, COMPort, channel): 9 | """ Constructor for the class. """ 10 | self.channel = self.format_chan_name(channel) 11 | self.gpib_address = 'GPIB'+str(gpib_num)+'::'+str(COMPort)+'::INSTR' 12 | self.resolution = self.getresolution(self.format_chan_name(channel)) 13 | self.name = 'AgilentE3631A::' + str(COMPort) + '::' + str(self.channel) 14 | self.connect() 15 | 16 | @staticmethod 17 | def format_chan_name(chan_name): 18 | """Function used to format the input channel name to something valid.""" 19 | if chan_name in [6, '6', '+6', 'p6', 'P6', 'P6V']: 20 | chan_name = 'P6V' 21 | elif chan_name in [25, '+25', 'p25', 'P25', 'P25V']: 22 | chan_name = 'P25V' 23 | elif chan_name in [-25, '-25', 'n25', 'n25', 'n25V']: 24 | chan_name = 'N25V' 25 | else: 26 | print('Error: Channel name is not valid.') 27 | return chan_name 28 | 29 | @staticmethod 30 | def getresolution(chan_name): 31 | """Get the resolution for each channel.""" 32 | if chan_name == 'P6V': 33 | resolution = 0.001 34 | elif chan_name == 'P25V': 35 | resolution = 0.01 36 | elif chan_name == 'N25V': 37 | resolution = 0.01 38 | return resolution 39 | 40 | def output_on(self): 41 | """Turn on the output corresponding to the source's channel.""" 42 | self.inst.write('OUTPut:STATe ON') 43 | 44 | def output_off(self): 45 | """Turn off the output corresponding to the source's channel.""" 46 | self.inst.write('OUTPut:STATe OFF') 47 | 48 | def source_voltage(self, bias): 49 | """Source the voltage by the corresponding output.""" 50 | self.inst.write('APPL ' + self.channel + ', ' + str(bias) + ', ' + str(1)) 51 | if bias != 0.: 52 | self.output_on() 53 | 54 | def set_range_low(self): 55 | self.inst.write('VOLTage:RANGe LOW') 56 | 57 | def set_range_high(self): 58 | self.inst.write('VOLTage:RANGe HIGH') 59 | 60 | def measure_current(self): 61 | """Measure the current flowing.""" 62 | return self.inst.query_ascii_values('MEASure:CURRent:DC? ' + self.channel)[0] 63 | 64 | def measure_voltage(self): 65 | """Measure the voltage flowing.""" 66 | return self.inst.query_ascii_values('MEASure:voltage:DC? ' + self.channel)[0] 67 | 68 | def measure_power(self): 69 | """Measure the power applied.""" 70 | return self.measure_voltage()*self.measure_current() 71 | 72 | def display_on(self): 73 | self.inst.write('DISPlay:WINDow:STATe ON') 74 | 75 | def display_off(self): 76 | self.inst.write('DISPlay:WINDow:STATe OFF') -------------------------------------------------------------------------------- /instruments/Agilent_E3646A.py: -------------------------------------------------------------------------------- 1 | from Instruments.Instrument_pyvisa import Instrument_pyvisa 2 | 3 | 4 | # DC source class 5 | class Agilent_E3646A(Instrument_pyvisa): 6 | """Creates a detector object to enable the operation of the Keithley 2612B SMU.""" 7 | 8 | chan_resolution = [0.01, 0.01] 9 | 10 | def __init__(self, COMPort, channel): 11 | self.channel = self.format_chan_name(channel) 12 | self.gpib_address = 'GPIB0::'+str(COMPort)+'::INSTR' 13 | self.resolution = self.getresolution(self.format_chan_name(channel)) 14 | 15 | @staticmethod 16 | def format_chan_name(chan_name): 17 | """Function used to format the input channel name to something valid.""" 18 | if chan_name in ['1', 1, 'out1', 'OUT1', 'output1']: 19 | chan_name = 'OUT1' 20 | elif chan_name in ['2', 2, 'out2', 'OUT2', 'output2']: 21 | chan_name = 'OUT2' 22 | else: 23 | print('Error: Channel name is not valid.') 24 | return chan_name 25 | 26 | @staticmethod 27 | def getresolution(chan_name): 28 | """Get the resolution for each channel.""" 29 | if chan_name == 'OUT1': 30 | resolution = 0.01 31 | elif chan_name == 'OUT2': 32 | resolution = 0.01 33 | return resolution 34 | 35 | def output_on(self): 36 | """Turn on the output corresponding to the source's channel.""" 37 | self.inst.write('output on') 38 | 39 | def output_off(self): 40 | """Turn off the output corresponding to the source's channel.""" 41 | self.inst.write('output off') 42 | 43 | def source_voltage(self, bias): 44 | """Source the voltage by the corresponding output.""" 45 | self.inst.write('INST:SEL ' + self.channel) 46 | self.inst.write('APPL ' + str(bias)) 47 | if bias != 0.: 48 | self.output_on() 49 | 50 | def set_range_low(self): 51 | self.inst.write('VOLTage:RANGe LOW') 52 | 53 | def set_range_high(self): 54 | self.inst.write('VOLTage:RANGe HIGH') 55 | 56 | if __name__=='__main__': 57 | pass -------------------------------------------------------------------------------- /instruments/CoBriteDX1.py: -------------------------------------------------------------------------------- 1 | import visa 2 | 3 | # DC source class 4 | class CoBriteDX1(object): 5 | """Creates a laser object to use the CoBrite Laser.""" 6 | 7 | def __init__(self, COM): 8 | self.COM = COM 9 | 10 | self.min_freq = 191.5000 11 | self.max_freq = 196.2500 12 | self.FTF_limit = 0.0000 13 | self.min_power = 6.00 14 | self.max_power = 18.00 15 | 16 | def connect(self): 17 | rm = visa.ResourceManager() 18 | self.inst = rm.open_resource("COM" + str(self.COM)) 19 | self.inst.baud_rate = 115200 20 | self.inst.data_bits = 8 21 | #x.parity = visa.constants.no_parity 22 | #x.stop_bits = visa.constants.VI_ASRL_STOP_ONE 23 | self.inst.flow_control = visa.constants.VI_ASRL_FLOW_NONE 24 | self.inst.timeout=10000 25 | 26 | def laser_on(self): 27 | """ Turn the output of the laser on.""" 28 | self.inst.write("STAT 1;",encoding="utf-8") 29 | print("Laser is turned on. Settling ...") 30 | 31 | def laser_off(self): 32 | """ Turn the output of the laser off.""" 33 | self.inst.write("STAT 0;",encoding="utf-8") 34 | print("Laser is turned off.") 35 | 36 | def set_power(self, power): 37 | self.inst.write("POW " + str(power) + ";") 38 | 39 | def set_wavelength(self, wavelength): 40 | self.inst.write("WAV " + str(wavelength) + ";") 41 | 42 | def set_frequency(self, frequency): 43 | self.inst.write("FRE" + str(frequency) + ";") -------------------------------------------------------------------------------- /instruments/ILX_LDT5910B.py: -------------------------------------------------------------------------------- 1 | from Instruments.Instrument_pyvisa import Instrument_pyvisa 2 | 3 | # DC source class 4 | class ILX_LDT5910B(Instrument_pyvisa): 5 | """Code to use the ILX-5910B Thermoelectric Temperature Controller remotely via GPIB.""" 6 | 7 | def __init__(self, gpib_num, COMPort): 8 | self.gpib_address = 'GPIB'+str(gpib_num)+'::'+str(COMPort)+'::INSTR' 9 | 10 | self.temp_setpoint = 25 11 | self.auto_on = False 12 | 13 | def set_temp(self, temperature): 14 | """Set the temperature set point for the TEC.""" 15 | self.inst.write("T " + str(temperature)) 16 | self.temp_setpoint = temperature 17 | 18 | def get_actual_temp(self): 19 | """Get the actual temperature measured by the TEC.""" 20 | return self.inst.query_ascii_values("T?")[0] 21 | 22 | def output_on(self): 23 | """Turn the TEC output on.""" 24 | self.inst.write("output 1") 25 | print("TEC output is on.") 26 | 27 | def output_off(self): 28 | """Turn the TEC output off.""" 29 | self.inst.write("output 0") 30 | print("TEC output is off.") 31 | 32 | def set_tolerance(self): 33 | pass 34 | 35 | def get_tolerance(self): 36 | tol = self.inst.query_ascii_values("TOL?") 37 | # temperature tolerance, time window 38 | return tol[0], tol[1] 39 | 40 | def sweep_temp(self): 41 | pass 42 | 43 | def display(self, mode): 44 | if mode == "actual temp": 45 | self.inst.write("DISplay:T") 46 | print("Display mode is now set to ACTUAL TEMP.") 47 | elif mode == "set temp": 48 | self.inst.write("DISplay:SET") 49 | print("Display mode is now set to SET TEMP.") 50 | else: 51 | print("Error: Invalid display mode.") 52 | 53 | def auto(self): 54 | """Toggles auto mode on and off.""" 55 | if self.auto_on == True: 56 | self.auto_on == False 57 | self.inst.write("DISplay:AUTO 0") 58 | print("AUTO mode is off.") 59 | elif self.auto_on == False: 60 | self.auto_on == True 61 | self.inst.write("DISplay:AUTO 1") 62 | print("AUTO mode is on.") 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /instruments/Instrument_pyvisa.py: -------------------------------------------------------------------------------- 1 | import visa 2 | 3 | class Instrument_pyvisa(object): 4 | """ 5 | General VISA instrument 6 | 7 | Attributes 8 | gpib_address : Remote address for GPIB connection in the format 'GPIB'+str(gpib_num)+'::'+str(COMPort)+'::INSTR" 9 | """ 10 | 11 | echo = False 12 | 13 | def connect(self): 14 | """Connect to the instrument's remote interface.""" 15 | rm = visa.ResourceManager() 16 | self.inst = rm.open_resource(self.gpib_address) 17 | if self.echo: print("Connected to the instrument.") 18 | 19 | def disconnect(self): 20 | """Disconnect the device and mark the handle as invalid.""" 21 | self.inst.close() 22 | if self.echo: print("Disconnected to the instrument.") 23 | 24 | def identify(self): 25 | """Print the instrument's identification.""" 26 | print(self.inst.query('*IDN?')) 27 | -------------------------------------------------------------------------------- /instruments/JDS_SB_Switch.py: -------------------------------------------------------------------------------- 1 | from Instruments.Instrument_pyvisa import Instrument_pyvisa 2 | 3 | class JDSopticalSwitch(Instrument_pyvisa): 4 | """ 5 | JDS Uniphase Fiber Optic Switch model SB [1x10] 6 | P/N : SB10B5-C2FP 7 | 8 | Use Case Example: 9 | switch = opticalSwitch(0, 7) 10 | switch.connect() 11 | switch.identify() 12 | switch.setChannel(1) 13 | """ 14 | 15 | echo = False 16 | 17 | def __init__(self, gpib_num, COMPort): 18 | 19 | self.gpib_address = 'GPIB'+str(gpib_num)+'::'+str(COMPort)+'::INSTR' 20 | 21 | def identify(self): 22 | """Print the instrument's identification.""" 23 | print(self.inst.query('IDN?')) 24 | 25 | def setChannel(self, channelNumber): 26 | """Closes the optical path represented by integer channelNumber. Makes an optical path with Common.""" 27 | self.inst.write('CLOSE {}'.format(channelNumber)) 28 | if self.echo: print('Switch path is now closed with channel {}.'.format(channelNumber)) 29 | 30 | def getChannel(self): 31 | """Returns the current optical path number.""" 32 | return self.inst.query('CLOSE?') 33 | 34 | def isOperationComplete(self): 35 | """Returns the status of the input buffer. True means that last operation is complete. """ 36 | operationComplete = False 37 | if int(self.inst.query('OPC?')) == 1: operationComplete = True 38 | return operationComplete 39 | 40 | def reset(self): 41 | """Returns the switch to power up state, for example, channel 0, relay drivers off.""" 42 | self.inst.write('RESET') 43 | if self.echo: print('The switch has been reset to it\'s original state.') 44 | 45 | def readStatusRegister(self): 46 | return self.inst.query('STB?') 47 | 48 | def readConditionRegister(self): 49 | return self.inst.query('CNB?') -------------------------------------------------------------------------------- /instruments/Keithley_2612B.py: -------------------------------------------------------------------------------- 1 | from Instruments.Instrument_pyvisa import Instrument_pyvisa 2 | 3 | # DC source class 4 | class Keithley_2612B(Instrument_pyvisa): 5 | """ 6 | Creates a detector object to enable the operation of the Keithley 2612B SMU. 7 | 8 | example: DC = Keithley_2612B(0,20,'a') 9 | """ 10 | 11 | voltage_range = [200e-3, 2, 20, 200] 12 | current_range = [100e-9, 1e-6, 10e-6, 100e-6, 1e-3, 10e-3, 100e-3, 1, 1.5, 10] 13 | 14 | def __init__(self, gpib_num, COMPort, channel): 15 | self.channel = self.format_chan_name(channel) 16 | self.gpib_address = 'GPIB'+str(gpib_num)+'::'+str(COMPort)+'::INSTR' 17 | self.name = 'Keithley2612B::' + str(COMPort) + '::' + str(self.channel) 18 | self.connect() 19 | 20 | def format_chan_name(self, chan_name): 21 | """Function used to format the input channel name to something valid.""" 22 | if chan_name in ['a', 'A', 0, 'chan a']: 23 | chan_name = 'a' 24 | elif chan_name in ['b', 'B', 1, 'chan b']: 25 | chan_name = 'b' 26 | else: 27 | print('Error: Channel name is not valid.') 28 | return chan_name 29 | 30 | 31 | # Range commands 32 | 33 | # Measure 34 | def measure_current_autorange_on(self): 35 | """Enable current measure autorange.""" 36 | self.inst.write("smu" + self.channel + ".measure.autorangei = smu" + self.channel + ".AUTORANGE_ON") 37 | 38 | def measure_current_autorange_off(self): 39 | """Disable current measure autorange.""" 40 | self.inst.write("smu" + self.channel + ".measure.autorangei = smu" + self.channel + ".AUTORANGE_OFF") 41 | 42 | def measure_voltage_autorange_on(self): 43 | """Enable voltage measure autorange.""" 44 | self.inst.write("smu" + self.channel + ".measure.autorangev = smu" + self.channel + ".AUTORANGE_ON") 45 | 46 | def measure_voltage_autorange_off(self): 47 | """Disable voltage measure autorange.""" 48 | self.inst.write("smu" + self.channel + ".measure.autorangev = smu" + self.channel + ".AUTORANGE_OFF") 49 | 50 | def measure_range_current(self,rangeval): 51 | """Set current measure range.""" 52 | self.inst.write("smu" + self.channel + ".measure.rangei = " + str(rangeval)) 53 | 54 | def measure_range_voltage(self,rangeval): 55 | """Set voltage measure range.""" 56 | self.inst.write("smu" + self.channel + ".measure.rangev = " + str(rangeval)) 57 | 58 | # Source 59 | def source_current_autorange_on(self): 60 | """Enable current measure autorange.""" 61 | self.inst.write("smu" + self.channel + ".source.autorangei = smu" + self.channel + ".AUTORANGE_ON") 62 | 63 | def source_current_autorange_off(self): 64 | """Disable current measure autorange.""" 65 | self.inst.write("smu" + self.channel + ".source.autorangei = smu" + self.channel + ".AUTORANGE_OFF") 66 | 67 | def source_voltage_autorange_on(self): 68 | """Enable voltage measure autorange.""" 69 | self.inst.write("smu" + self.channel + ".source.autorangev = smu" + self.channel + ".AUTORANGE_ON") 70 | 71 | def source_voltage_autorange_off(self): 72 | """Disable voltage measure autorange.""" 73 | self.inst.write("smu" + self.channel + ".source.autorangev = smu" + self.channel + ".AUTORANGE_OFF") 74 | 75 | def source_range_current(self,rangeval): 76 | """Set current measure range.""" 77 | self.inst.write("smu" + self.channel + ".source.rangei = " + str(rangeval)) 78 | 79 | def source_range_voltage(self,rangeval): 80 | """Set voltage measure range.""" 81 | self.inst.write("smu" + self.channel + ".source.rangev = " + str(rangeval)) 82 | 83 | def limit_current(self, level): 84 | """Set current limit.""" 85 | self.inst.write("smu" + self.channel + ".source.limiti = " + str(level)) 86 | 87 | def limit_voltage(self, level): 88 | """Set voltage limit.""" 89 | self.inst.write("smu" + self.channel + ".source.limitv = " + str(level)) 90 | 91 | def limit_power(self, level): 92 | """Set power limit.""" 93 | self.inst.write("smu" + self.channel + ".source.limitp = " + str(level)) 94 | 95 | 96 | # Measure commands 97 | def measure_current(self): 98 | """Request a current reading.""" 99 | return self.inst.query_ascii_values("print(smu" + self.channel + ".measure.i())")[0] 100 | 101 | def measure_voltage(self): 102 | """Request a voltage reading.""" 103 | return self.inst.query_ascii_values("print(smu" + self.channel + ".measure.v())") 104 | 105 | def measure_currentvoltage(self): 106 | """Request a current and voltage reading.""" 107 | return self.inst.query_ascii_values("print(smu" + self.channel + ".measure.iv())") 108 | 109 | def measure_resistance(self): 110 | """Request a resistance reading.""" 111 | return self.inst.query_ascii_values("print(smu" + self.channel + ".measure.r())") 112 | 113 | def measure_power(self): 114 | """Request a power reading.""" 115 | return self.inst.query_ascii_values("print(smu" + self.channel + ".measure.p())") 116 | 117 | 118 | # Source commands 119 | def function_voltage(self): 120 | """Select voltage source function.""" 121 | return self.inst.write("smu" + self.channel + ".source.func = smu" + self.channel + ".OUTPUT_DCVOLTS") 122 | 123 | def function_current(self): 124 | """Select current source function.""" 125 | return self.inst.write("smu" + self.channel + ".source.func = smu" + self.channel + ".OUTPUT_DCAMPS") 126 | 127 | def source_current(self, bias): 128 | """Set current source value.""" 129 | self.inst.write("smu" + self.channel + ".source.leveli = " + str(bias)) 130 | 131 | def source_voltage(self, bias): 132 | """Set voltage source value.""" 133 | self.inst.write("smu" + self.channel + ".source.levelv = " + str(bias)) 134 | 135 | def output_on(self): 136 | """Turn on source output.""" 137 | self.inst.write("smu" + self.channel + ".source.output = 1") 138 | 139 | def output_off(self): 140 | """Turn off source output.""" 141 | self.inst.write("smu" + self.channel + ".source.output = 0") 142 | 143 | 144 | # Misc commands 145 | def factory_reset(self): 146 | """Restore Series 2600B defaults.""" 147 | self.inst.write("smu" + self.channel + ".reset()") 148 | 149 | def set_filter(self, count, filter_type): 150 | self.inst.write("smu" + self.channel + ".measure.filter.count = " + str(count)) 151 | if filter_type == "median": 152 | self.inst.write("smu" + self.channel + ".measure.filter.type = smua.FILTER_MEDIAN") 153 | elif filter_type == "moving average": 154 | self.inst.write("smu" + self.channel + ".measure.filter.type = smua.FILTER_MOVING_AVG") 155 | elif filter_type == "repeat average": 156 | self.inst.write("smu" + self.channel + ".measure.filter.type = smua.FILTER_REPEAT_AVG") 157 | 158 | def filter_on(self): 159 | self.inst.write("smu" + self.channel + ".measure.filter.enable = smua.FILTER_ON") 160 | 161 | def filter_off(self): 162 | self.inst.write("smu" + self.channel + ".measure.filter.enable = smua.FILTER_OFF") 163 | 164 | def filter_state(self): 165 | print(self.inst.query_ascii_values("print(smu" + self.channel + ".measure.filter.enable)")) -------------------------------------------------------------------------------- /instruments/TCPinterface.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python 2 | # -*- coding : utf-8 -*- 3 | 4 | __author__ = 'Sizhan Liu' 5 | __version__ = "1.0" 6 | 7 | ''' 8 | This class is for TCP/IP interface. 9 | Including: 10 | 1. Creating/Close TCP connection. 11 | 2. Basic TCP writing, reading, querying. 12 | 13 | reference: 14 | 1. https://bitbucket.org/martijnj/telepythic Copyright 2014 by Martijn Jasperse 15 | 16 | ''' 17 | import socket 18 | import select 19 | import time 20 | 21 | class TCP(object): 22 | def __init__(self, host, port): 23 | self.__host = host 24 | self.__port = port 25 | 26 | # === Connect === 27 | 28 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) 29 | self.sock.settimeout(10) 30 | try: 31 | self.sock.connect((self.__host, self.__port)) 32 | except: 33 | raise socket.timeout("Fail to connect to %s:%i" %(self.__host,self.__port)) 34 | 35 | def TCP_close(self): 36 | '''close TCP connection ''' 37 | self.sock.close() 38 | print(self.__class__.__name__ + ' connection is closed.') 39 | 40 | 41 | def has_reply(self, timeout=0): 42 | '''check whether a replay is waiting to be read''' 43 | socklist = select.select([self.sock], [], [], timeout) 44 | return len(socklist[0])>0 45 | 46 | def write(self, cmd): 47 | strtmp = str(cmd) 48 | self.sock.send(strtmp.encode()) 49 | time.sleep(0.5) 50 | 51 | def read_raw(self): 52 | ''' 53 | Read data more than 1024B, return fromat is strings. 54 | Add 300ms delay to avoid TCP transfer data miss. 55 | ''' 56 | data = self.sock.recv(256) 57 | time.sleep(0.3) 58 | try: 59 | while self.has_reply(timeout=0): 60 | data += self.sock.recv(256) 61 | return data 62 | except: 63 | print ("Recv time out") 64 | 65 | 66 | def clearbuffer(self): 67 | '''clear TCP buffer if type wrong command''' 68 | n = 0 69 | while self.has_reply(timeout=0): 70 | n += len(self.sock.recv(1024)) 71 | return n 72 | 73 | def query(self, cmd): 74 | '''query result from Luna''' 75 | self.write(cmd) 76 | data = self.read_raw() 77 | return data 78 | -------------------------------------------------------------------------------- /instruments/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This package contains all the instruments required by the software. 3 | """ 4 | 5 | # modules imported with the " from Instruments import * " statement 6 | #__all__ = ["Instrument","Instrument_pyvisa","Agilent_81635A","Agilent_E3646A","Agilent_E3631A","hp816x_instr","Keithley_2612B","laser"] 7 | 8 | # directly import classes from modules 9 | #from Instruments.Instrument import Instrument 10 | #from Instruments.Instrument_pyvisa import Instrument_pyvisa 11 | #from Instruments.Agilent_81635A import Agilent_81635A 12 | #from Instruments.Agilent_E3646A import Agilent_E3646A 13 | #from Instruments.Agilent_E3631A import Agilent_E3631A 14 | #from Instruments.hp816x_instr import hp816x 15 | #from Instruments.Keithley_2612B import Keithley_2612B 16 | #from Instruments.laser import laser -------------------------------------------------------------------------------- /instruments/smarPod/GUI_gettingstarted_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # import tkinter as tk 5 | # import webbrowser 6 | # 7 | # 8 | # class MainAppView(tk.Frame): 9 | # """Encapsulates of all the GUI logic. 10 | # 11 | # Attributes: 12 | # master: where to open the Frame, by deafult root window 13 | # title: Main Label 14 | # 15 | # one: Button 16 | # two: Button 17 | # three: Button 18 | # """ 19 | # 20 | # def start_gui(self, ok=True): 21 | # """Starts the GUI, if everything ok , to change 22 | # """ 23 | # 24 | # if ok: 25 | # self.mainloop() 26 | # else: 27 | # self.master.destroy() 28 | # 29 | # def createWidgets(self): 30 | # """Create the set of initial widgets. 31 | # 32 | # """ 33 | # 34 | # # Create the label 35 | # 36 | # self.title = tk.Label( 37 | # self, text=" What's up ?") 38 | # self.title.grid( 39 | # row=0, column=0, columnspan=4, sticky=tk.E + tk.W) 40 | # 41 | # # Create the three buttons 42 | # 43 | # self.one = tk.Button(self) 44 | # self.one["text"] = "Task 1" 45 | # self.one.grid(row=1, column=0) 46 | # 47 | # self.two = tk.Button(self) 48 | # self.two["text"] = "Task 2" 49 | # self.two.grid(row=1, column=1) 50 | # 51 | # self.three = tk.Button(self) 52 | # self.three["text"] = "Task 3" 53 | # self.three.grid(row=1, column=2) 54 | # 55 | # self.four = tk.Button(self) 56 | # self.four["text"] = "Task 4" 57 | # self.four.grid(row=1, column=3) 58 | # 59 | # def __init__(self, master=None): 60 | # tk.Frame.__init__(self, master) 61 | # self.grid() 62 | # # option is needed to put the main label in the window 63 | # 64 | # 65 | # self.createWidgets() 66 | # 67 | # 68 | # ######################################################################################################################## 69 | # # from MainAppView import MainAppView 70 | # 71 | # 72 | # class MainAppController(object): 73 | # def nothing(self): 74 | # pass 75 | # 76 | # def init_view(self, root): 77 | # """Initializes GUI view 78 | # In addition it bindes the Buttons with the callback methods. 79 | # 80 | # """ 81 | # self.view = MainAppView(master=root) 82 | # 83 | # # Bind buttons with callback methods 84 | # self.view.one["command"] = self.nothing 85 | # self.view.two["command"] = self.nothing 86 | # self.view.three["command"] = self.nothing 87 | # self.view.four["command"] = self.nothing 88 | # 89 | # # Start the gui 90 | # 91 | # 92 | # self.view.start_gui() 93 | # 94 | # ######################################################################################################################## 95 | # 96 | # def main(): 97 | # 98 | # controller = MainAppController() 99 | # 100 | # # Build Gui and start it 101 | # root = tk.Tk() 102 | # root.title('Main Application') 103 | # 104 | # controller.init_view(root) 105 | # 106 | # 107 | # print('Bye Bye') 108 | # 109 | # 110 | # 111 | # if __name__ == "__main__": 112 | # main() 113 | ######################################################################################################################### 114 | ######################################################################################################################### 115 | 116 | # 117 | # from tkinter import * 118 | # """A button - what can we do with it? 119 | # Well lets make it change the colour of the text label 120 | # and change the style of the button when it is pressed""" 121 | # 122 | # class OnOffButton(Button): 123 | # def __init__(self,master=None,onText=None,offText=None): 124 | # self.onText = onText 125 | # self.offText = offText 126 | # Button.__init__(self,master,text=self.offText) 127 | # self['command'] = self._onButtonClick 128 | # self['width'] = max(len(self.onText),len(self.offText)) 129 | # def _onButtonClick(self): 130 | # """This method is called when the start button is pressed. 131 | # If the button colour is the default, set it to red and sink the button 132 | # Otherwise set it to the default again and raise the button""" 133 | # if self['fg'] == "SystemButtonText": 134 | # self['fg'] = "red" 135 | # self['relief'] = "sunken" 136 | # self['text'] = self.onText 137 | # else: 138 | # self['fg'] = "SystemButtonText" 139 | # self['relief'] = "raised" 140 | # self['text'] = self.offText 141 | # 142 | # class App(Frame): 143 | # """Our Application. We inherit all the properties 144 | # and methods of a Tkinter Frame""" 145 | # def __init__(self,master=None): 146 | # Frame.__init__(self,master) 147 | # self.lblHello = Label(self,text="Hello World") 148 | # self.lblHello['fg'] = "red" 149 | # self.lblHello.grid() 150 | # self.btnStart = OnOffButton(self,onText="Click Me Again",offText="Click Me") 151 | # self.btnStart.grid() 152 | # self.btn2 = OnOffButton(self,onText="Disable",offText="Enable") 153 | # self.btn2.grid() 154 | # 155 | # def main(): 156 | # root = Tk() 157 | # app = App(master=root) 158 | # app.grid() 159 | # 160 | # """This is the code that is executed by python when we run this .py file""" 161 | # if __name__ == '__main__': 162 | # main() 163 | 164 | ######################################################################################################################### 165 | ######################################################################################################################## 166 | import tkinter as root 167 | 168 | 169 | class Dashboard(): 170 | def __init__(self, title, length, breadth): 171 | self.window = root.Tk() 172 | self.window.title(title) 173 | 174 | # get screen width and height 175 | ws = self.window.winfo_screenwidth() 176 | hs = self.window.winfo_screenheight() 177 | # calculate position x, y 178 | x = (ws / 2) - (length / 2) 179 | y = (hs / 2) - (breadth / 2) 180 | self.window.geometry('%dx%d+%d+%d' % (length, breadth, x, y)) 181 | 182 | # To show the main window with widget like checkbox, buttons etc. 183 | 184 | def Display(self, choice=True): 185 | self.window.mainloop() 186 | return() 187 | 188 | Dashboard('allo',1,1) 189 | 190 | Dashboard.Display -------------------------------------------------------------------------------- /instruments/smarPod/SmarPodFrame.py: -------------------------------------------------------------------------------- 1 | 2 | import wx 3 | import ctypes 4 | 5 | # Panel that appears in the main window which contains the controls for the Corvus motor. 6 | class topSmarpodPanel(wx.Panel): 7 | def __init__(self, parent, motor): 8 | super(topSmarpodPanel, self).__init__(parent) 9 | self.motor = motor 10 | self.maxaxis = motor.NumberOfAxis+1 11 | self.InitUI() 12 | # POS = motor.Pos 13 | 14 | def InitUI(self): 15 | sb = wx.StaticBox(self, label='SmarPod'); 16 | vbox = wx.StaticBoxSizer(sb, wx.VERTICAL) 17 | btnStop = wx.Button(self, label='Stop', size=(50, 20)) 18 | vbox.Add(btnStop, proportion=0, flag=wx.EXPAND | wx.ALIGN_RIGHT, border=8) 19 | btnStop.Bind(wx.EVT_BUTTON, self.stopsmarpod) 20 | self.SetSizer(vbox) 21 | 22 | hbox = wx.BoxSizer(wx.HORIZONTAL) 23 | hbox1 = wx.BoxSizer(wx.HORIZONTAL) 24 | 25 | 26 | 27 | 28 | # hbox1.AddMany([(st1, 1, wx.EXPAND), (self.Setparam, 1, wx.EXPAND)]) 29 | # st1 = wx.StaticText(self, label='Set') 30 | self.Setparam = wx.Button(self, label='Set', size=(50, 20)) 31 | self.Setparam.Bind(wx.EVT_BUTTON, self.Setalign) 32 | self.para7 = wx.BoxSizer(wx.VERTICAL) 33 | self.para7name = wx.StaticText(self, label='Align method') 34 | self.para7tc = wx.ComboBox(self, choices=['PosXY', 'PosOXOZ','PosOXOY']) 35 | self.para7.AddMany([(self.para7name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para7tc, 1, wx.EXPAND | wx.ALIGN_CENTER),(self.Setparam, 1, wx.EXPAND | wx.ALIGN_LEFT)]) 36 | 37 | # st2 = wx.StaticText(self, label='Set') 38 | self.Setparam2 = wx.Button(self, label='Set', size=(50, 20)) 39 | self.Setparam2.Bind(wx.EVT_BUTTON, self.Setspeed) 40 | self.para5 = wx.BoxSizer(wx.VERTICAL) 41 | self.para5name = wx.StaticText(self, label='Speed [m/s]') 42 | self.para5tc = wx.TextCtrl(self, value='0.002') 43 | self.para5.AddMany([(self.para5name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para5tc, 1, wx.EXPAND | wx.ALIGN_CENTER),(self.Setparam2, 1, wx.EXPAND | wx.ALIGN_LEFT)]) 44 | 45 | 46 | # st3 = wx.StaticText(self, label='Set') 47 | self.Setparam3 = wx.Button(self, label='Set', size=(50, 20)) 48 | self.Setparam3.Bind(wx.EVT_BUTTON, self.setpivot) 49 | 50 | self.para3 = wx.BoxSizer(wx.VERTICAL) 51 | self.para3n = wx.BoxSizer(wx.HORIZONTAL) 52 | self.para3name = wx.StaticText(self, label='Px') 53 | self.para33name = wx.StaticText(self, label='Py') 54 | self.para333name = wx.StaticText(self, label='Pz') 55 | self.para3v = wx.BoxSizer(wx.HORIZONTAL) 56 | self.para3tc = wx.TextCtrl(self, value='-466.884') 57 | self.para33tc = wx.TextCtrl(self, value='20633.60') 58 | self.para333tc = wx.TextCtrl(self, value='4988.999') 59 | self.para3v.AddMany([(self.para3tc, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para33tc, 1, wx.EXPAND | wx.ALIGN_CENTER),(self.para333tc, 1, wx.EXPAND | wx.ALIGN_LEFT)]) 60 | self.para3n.AddMany([(self.para3name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para33name, 1, wx.EXPAND | wx.ALIGN_CENTER),(self.para333name, 1, wx.EXPAND | wx.ALIGN_LEFT)]) 61 | self.para3.AddMany([(self.para3n, 1, wx.EXPAND), (self.para3v, 1, wx.EXPAND), (self.Setparam3, 1, wx.EXPAND)]) 62 | 63 | 64 | vbox.AddMany([(self.para5, 0, wx.EXPAND),(self.para7, 0, wx.EXPAND), (self.para3, 0, wx.EXPAND)]) 65 | hbox.AddMany([(self.Setparam, 0, wx.ALIGN_RIGHT)]) 66 | self.SetSizer(vbox) 67 | axis = 1 68 | for motorCtrl in range(axis, self.maxaxis): 69 | motorPanel = SmarPodPanel(self, motorCtrl, axis) 70 | motorPanel.motor = self.motor 71 | vbox.Add(motorPanel, flag=wx.LEFT | wx.TOP | wx.ALIGN_LEFT, border=0, proportion=0) 72 | vbox.Add((-1, 2)) 73 | sl = wx.StaticLine(self) 74 | vbox.Add(sl, flag=wx.EXPAND, border=0, proportion=0) 75 | vbox.Add((-1, 2)) 76 | axis = axis + 1 77 | 78 | self.SetSizer(vbox) 79 | 80 | 81 | def Setspeed(self, event): 82 | self.motor.setVelocity(float(self.para5tc.GetValue())) 83 | 84 | def Setalign(self, event): 85 | align = {} 86 | align['PosXY'] = 0 87 | align['PosOXOZ'] = 1 88 | align['PosOXOY'] = 2 89 | self.motor.alignOption(align[self.para7tc.GetValue()]) 90 | 91 | def stopsmarpod(self,event): 92 | self.motor.stopSmarpod 93 | 94 | def setpivot(self,event): 95 | self.motor.setPivot(float(self.para3tc.GetValue()), float(self.para333tc.GetValue()),float(self.para33tc.GetValue())) 96 | 97 | 98 | class SmarPodPanel(wx.Panel): 99 | def __init__(self, parent, motorCtrl, axis): 100 | super(SmarPodPanel, self).__init__(parent) 101 | self.motorCtrl = motorCtrl; 102 | self.axis = axis; 103 | self.InitUI() 104 | 105 | def InitUI(self): 106 | hbox = wx.BoxSizer(wx.HORIZONTAL) 107 | st1 = wx.StaticText(self, label='Motor Control')#+' '+ Pos[self.axis-1]) 108 | hbox.Add(st1, flag=wx.ALIGN_LEFT, border=8) 109 | st1 = wx.StaticText(self, label='') 110 | hbox.Add(st1, flag=wx.EXPAND, border=8, proportion=1) 111 | btn1 = wx.Button(self, label='-', size=(20, 20)) 112 | hbox.Add(btn1, flag=wx.EXPAND | wx.RIGHT, proportion=0, border=8) 113 | btn1.Bind(wx.EVT_BUTTON, self.OnButton_MinusButtonHandler) 114 | 115 | self.tc = wx.TextCtrl(self, value='0') #str(self.axis)) # change str(self.axis) to '0' 116 | hbox.Add(self.tc, proportion=2, flag=wx.EXPAND) 117 | 118 | st1 = wx.StaticText(self, label='um') 119 | hbox.Add(st1, flag=wx.ALIGN_LEFT, border=8) 120 | 121 | btn2 = wx.Button(self, label='+', size=(20, 20)) 122 | hbox.Add(btn2, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=8) 123 | btn2.Bind(wx.EVT_BUTTON, self.OnButton_PlusButtonHandler) 124 | self.SetSizer(hbox); 125 | 126 | # sb = wx.StaticBox(self, label='SmarPod Connection Parameters'); 127 | # vbox = wx.StaticBoxSizer(sb, wx.VERTICAL) 128 | # self.para5 = wx.BoxSizer(wx.HORIZONTAL) 129 | # self.para5name = wx.StaticText(self, label= 'Speed [m/s]') 130 | # self.para5tc = wx.TextCtrl(self, value='0.002') 131 | # self.para5.AddMany( 132 | # [(self.para5name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para5tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 133 | # vbox.AddMany([self.para5, 0, wx.EXPAND]) 134 | # self.SetSizer(vbox); 135 | 136 | 137 | def getMoveValue(self): 138 | try: 139 | val = float(self.tc.GetValue()); 140 | except ValueError: 141 | self.tc.SetValue('0'); 142 | return 0.0; 143 | return val; 144 | 145 | def OnButton_MinusButtonHandler(self, event): 146 | 147 | if self.axis == 1: 148 | self.motor.moveAbsoluteXY(-1 *(self.getMoveValue()),0,0,0,0,0) 149 | print("Axis X Moved") 150 | 151 | if self.axis == 2: 152 | self.motor.moveAbsoluteXY(0,-1* self.getMoveValue(),0,0,0,0) 153 | print("Axis Y Moved") 154 | if self.axis == 3: 155 | self.motor.moveAbsoluteXY(0,0,-1* self.getMoveValue(),0,0,0) 156 | print("Axis Z Moved") 157 | if self.axis == 4: 158 | self.motor.moveAbsoluteXY(0,0,0,-1* self.getMoveValue(),0,0) 159 | print("Angle Theta OX Moved") 160 | if self.axis == 5: 161 | self.motor.moveAbsoluteXY(0,0,0,0,-1* self.getMoveValue(),0) 162 | print("Angle Theta OY Moved") 163 | if self.axis == 6: 164 | self.motor.moveAbsoluteXY(0,0,0,0,0,-1*self.getMoveValue()) 165 | print("Angle Theta OZ Moved") 166 | 167 | def OnButton_PlusButtonHandler(self, event): 168 | if self.axis == 1: 169 | self.motor.moveAbsoluteXY(self.getMoveValue(),0,0,0,0,0) 170 | print("Axis X Moved") 171 | if self.axis == 2: 172 | self.motor.moveAbsoluteXY(0,self.getMoveValue(),0,0,0,0) 173 | print("Axis Y Moved") 174 | if self.axis == 3: 175 | self.motor.moveAbsoluteXY(0,0,self.getMoveValue(),0,0,0) 176 | print("Axis Z Moved") 177 | if self.axis == 4: 178 | self.motor.moveAbsoluteXY(0,0,0,self.getMoveValue(),0,0) 179 | print("Angle Theta OX Moved") 180 | if self.axis == 5: 181 | self.motor.moveAbsoluteXY(0,0,0,0,self.getMoveValue(),0) 182 | print("Angle Theta OY Moved") 183 | if self.axis == 6: 184 | self.motor.moveAbsoluteXY(0,0,0,0,0,self.getMoveValue()) 185 | print("Angle Theta OZ Moved") -------------------------------------------------------------------------------- /instruments/smarPod/SmarPodParameters.py: -------------------------------------------------------------------------------- 1 | import wx 2 | 3 | import SmarPodClass 4 | import SmarPodFrame 5 | import visa 6 | import ctypes 7 | 8 | # Panel in the Connect Instruments window which contains the connection settings for the Corvus Eco. 9 | class SmarPodParameters(wx.Panel): 10 | name = 'Stage: SmarPod' 11 | 12 | def __init__(self, parent, connectPanel, **kwargs): 13 | super(SmarPodParameters, self).__init__(parent) 14 | self.connectPanel = connectPanel 15 | self.InitUI() 16 | 17 | 18 | def InitUI(self): 19 | sb = wx.StaticBox(self, label='SmarPod Connection Parameters'); 20 | vbox = wx.StaticBoxSizer(sb, wx.VERTICAL) 21 | hbox = wx.BoxSizer(wx.HORIZONTAL) 22 | 23 | # First Parameter: Serial Port 24 | self.para1 = wx.BoxSizer(wx.HORIZONTAL) 25 | self.para1name = wx.StaticText(self, label='Serial Port') 26 | # self.para1tc = wx.ComboBox(self, choices=visa.ResourceManager().list_resources()) 27 | self.para1tc = wx.TextCtrl(self, value="network:192.168.1.200:5000") 28 | self.para1.AddMany( 29 | [(self.para1name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para1tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 30 | 31 | # Second Parameter: Number of Axis 32 | self.para2 = wx.BoxSizer(wx.HORIZONTAL) 33 | self.para2name = wx.StaticText(self, label='Number of Axis') 34 | self.para2tc = wx.TextCtrl(self, value='6') 35 | self.para2.AddMany( 36 | [(self.para2name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para2tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 37 | 38 | # 3e Parameter: Securty distance 39 | self.para3 = wx.BoxSizer(wx.HORIZONTAL) 40 | self.para3name = wx.StaticText(self, label=' Security distance [um]') 41 | self.para3tc = wx.TextCtrl(self, value='0') 42 | self.para3.AddMany( 43 | [(self.para3name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para3tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 44 | 45 | # 4e Parameter: Max Freq 46 | self.para4 = wx.BoxSizer(wx.HORIZONTAL) 47 | self.para4name = wx.StaticText(self, label='Maximal Frequency [Hz]') 48 | self.para4tc = wx.TextCtrl(self, value='18500') 49 | self.para4.AddMany( 50 | [(self.para4name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para4tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 51 | 52 | # 5e Parameter: Speed 53 | self.para5 = wx.BoxSizer(wx.HORIZONTAL) 54 | self.para5name = wx.StaticText(self, label= 'Speed [m/s]') 55 | self.para5tc = wx.TextCtrl(self, value='0.002') 56 | self.para5.AddMany( 57 | [(self.para5name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para5tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 58 | 59 | # 6e Parameter: Acceleration 60 | self.para6 = wx.BoxSizer(wx.HORIZONTAL) 61 | self.para6name = wx.StaticText(self, label= 'Acceleration [m/s]') 62 | self.para6tc = wx.TextCtrl(self, value='0.5') 63 | self.para6.AddMany( 64 | [(self.para6name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para6tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 65 | 66 | # 7e Parameter: Pivot 67 | self.para7 = wx.BoxSizer(wx.HORIZONTAL) 68 | self.para7name = wx.StaticText(self, label= 'Pivot [m]') 69 | self.para7tc = wx.ComboBox(self, choices=['SMARPOD_PIVOT_RELATIVE','SMARPOD_PIVOT_FIXED']) 70 | self.para7.AddMany( 71 | [(self.para7name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para7tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 72 | 73 | # 8e Parameter: Calibration 74 | self.para8 = wx.BoxSizer(wx.HORIZONTAL) 75 | self.para8name = wx.StaticText(self, label= 'Calibration') 76 | # self.para8tc = wx.StaticText(self, choices=['Yes', 'No']) 77 | self.para8tc = wx.ComboBox(self, choices=['Yes','No']) 78 | self.para8.AddMany( 79 | [(self.para8name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para8tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 80 | 81 | # 9e Parameter: model 82 | self.S =SmarPodClass.SmarPodClass() 83 | self.para9 = wx.BoxSizer(wx.HORIZONTAL) 84 | self.para9name = wx.StaticText(self, label='Model' ) 85 | self.para9tc = wx.StaticText(self, label= 'Model:'+str(self.S.model)+' ' + 'Name:' + str(self.S.name) ) 86 | # self.para9tc = wx.ComboBox(self, choices=['Yes','No']) 87 | self.para9.AddMany( 88 | [(self.para9name, 1, wx.EXPAND | wx.ALIGN_LEFT), (self.para9tc, 1, wx.EXPAND | wx.ALIGN_RIGHT)]) 89 | 90 | 91 | self.disconnectBtn = wx.Button(self, label='Disconnect') 92 | self.disconnectBtn.Bind(wx.EVT_BUTTON, self.disconnect) 93 | self.disconnectBtn.Disable() 94 | 95 | self.connectBtn = wx.Button(self, label='Connect') 96 | self.connectBtn.Bind(wx.EVT_BUTTON, self.connect) 97 | 98 | hbox.AddMany([(self.disconnectBtn, 0, wx.ALIGN_RIGHT), (self.connectBtn, 0, wx.ALIGN_RIGHT)]) 99 | vbox.AddMany([(self.para1, 0, wx.EXPAND), (self.para2, 0, wx.EXPAND), (self.para3, 0, wx.EXPAND), (self.para4, 0, wx.EXPAND), 100 | (self.para5, 0, wx.EXPAND),(self.para6, 0, wx.EXPAND), (self.para7, 0, wx.EXPAND), (self.para8, 0, wx.EXPAND), (self.para9, 0, wx.EXPAND), (hbox, 0, wx.ALIGN_BOTTOM)]) 101 | 102 | self.SetSizer(vbox) 103 | 104 | def connect(self, event): 105 | self.stage = SmarPodClass.SmarPodClass() 106 | self.sensor_mode = self.stage.SMARPOD_SENSORS_ENABLED 107 | pivot = {} 108 | pivot['SMARPOD_PIVOT_RELATIVE'] = self.stage.SMARPOD_PIVOT_RELATIVE 109 | pivot['SMARPOD_PIVOT_FIXED'] = self.stage.SMARPOD_PIVOT_FIXED 110 | calibration = {'Yes':1, 'No':0} 111 | speed = float(self.para5tc.GetValue()) 112 | acc = float(self.para6tc.GetValue()) 113 | # self.stage.connect(str(self.para1tc.GetValue()), visa.ResourceManager(), 5, 500, self.sensor_mode,8000) # int(self.para2tc.GetValue())) 114 | self.stage.connect(str(self.para1tc.GetValue()), speed, acc, self.sensor_mode, self.para4tc.GetValue(), pivot[str((self.para7tc.GetValue()))], calibration[str((self.para8tc.GetValue()))]) #int(self.para2tc.GetValue())) 115 | self.stage.panelClass = SmarPodFrame.topSmarpodPanel 116 | self.connectPanel.instList.append(self.stage) 117 | self.disconnectBtn.Enable() 118 | self.connectBtn.Disable() 119 | 120 | def disconnect(self, event): 121 | self.stage.disconnect() 122 | if self.stage in self.connectPanel.instList: 123 | self.connectPanel.instList.remove(self.stage) 124 | self.disconnectBtn.Disable() 125 | self.connectBtn.Enable() 126 | -------------------------------------------------------------------------------- /instruments/smarPod/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/instruments/smarPod/__init__.py -------------------------------------------------------------------------------- /instruments/smarPod/func.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import ctypes 3 | # import multiprocessing as mp 4 | # import time 5 | from ctypes import * 6 | def SmarpodInfo(lib): 7 | # Get DLL version 8 | Minor = c_int32() 9 | Major = c_int32() 10 | Update = c_int32() 11 | minor = pointer(Minor) 12 | major = pointer(Major) 13 | update = pointer(Update) 14 | Smarpod_GetDLLVersion = lib.Smarpod_GetDLLVersion 15 | Smarpod_GetDLLVersion(major, minor, update) 16 | # print("Smarpod_GetDLLVersion : ", Major.value, ".", Minor.value, ".", Update.value) 17 | dll = [Major.value, Minor.value,Update.value] 18 | # Get Model 19 | ModelList = (c_uint * 128)() 20 | listsize = c_uint(128) 21 | listsize_p = pointer(listsize) 22 | lib.Smarpod_GetModels(ModelList, listsize_p) 23 | model = ModelList[1] 24 | # Get model name 25 | Name = c_char_p() 26 | name = pointer(Name) 27 | lib.Smarpod_GetModelName(model, name) 28 | name = Name.value 29 | return name, model, dll 30 | 31 | def MakeSeq(xinit,yinit,zinit,oxinit,oyinit,ozinit,xspan, yspan, zspan,oxspan,oyspan,ozspan,dx,dy,dz): 32 | # Coordonnees limites 33 | xmin, xmax = (xinit - xspan / 2), (xinit + xspan / 2) 34 | ymin, ymax = (yinit - yspan / 2), (yinit + yspan / 2) 35 | zmin, zmax = (zinit - zspan / 2), (zinit + zspan / 2) 36 | oxmin, oxmax = (oxinit - oxspan / 2), (oxinit + oxspan / 2) 37 | oymin, oymax = (oyinit - oyspan / 2), (oyinit + oyspan / 2) 38 | ozmin, ozmax = (ozinit - ozspan / 2), (ozinit + ozspan / 2) 39 | x = np.linspace(xmin, xmax, round(xspan / dx)) 40 | y = np.linspace(ymin, ymax, round(yspan / dy)) 41 | z = np.linspace(zmin, zmax, round(zspan / dz)) 42 | ox = oxinit #np.linspace(oxinit, oxinit, round(oxspan / dox)) 43 | oy = oyinit #np.linspace(oyinit, oyinit, round(oyspan / doy)) 44 | oz = ozinit # np.linspace(ozinit, ozinit, round(ozspan / doz)) 45 | I = {} 46 | n=0 47 | for j in range(len(z)*len(y)): 48 | if j % 2 == 0: 49 | I[j] = x[::1] 50 | else: 51 | I[j] = x[::-1] 52 | 53 | A=[0]*(len(I)*len(x)) 54 | n=0 55 | for h in range(len(I)): 56 | for k in range(len(I[1])): 57 | A[n] = I[h][k] 58 | n=n+1 59 | p = {} 60 | n = 0 61 | for j in range(len(y)): 62 | for k in range(len(z)): 63 | for i in range(len(A)): 64 | x = A 65 | p[n] = [x[i], y[j], z[k],ox,oy,oz] 66 | n= n+1 67 | p = [v for v in p.values()] 68 | # print(np.array(p)) 69 | # print(len(p) == len(x)*len(z)*len(y)) 70 | return p 71 | # def MakePosTable(): 72 | # 73 | # 74 | # POS_DATA = [pos_data1, pos_data2, pos_data3] 75 | # POS_DATA = np.reshape(POS_DATA,(len(POS_DATA),6)) 76 | # 77 | #Create liste balayage hexapod 78 | # n_init = 5 79 | # key = np.arange(5) 80 | # val = (np.zeros(3) * n_init) 81 | # p_init = {key[i] : val[i] for i in range(5)} 82 | # p_init = (np.zeros(3) * n_init) 83 | # class ListPos: 84 | # n_init = 1 85 | # p_init = {} 86 | # p_init = (np.zeros(3)*n_init) 87 | # def __init__(self, n = n_init, a = p_init): 88 | # self.n = n 89 | # self.p = a 90 | # self.vec_init = np.zeros(self.n * 3) 91 | # self.table = np.reshape(self.p, (self.n, 3)) 92 | # def f(self): 93 | # # self.s = np.zeros(len(p)*3) 94 | # seq = self.table 95 | # # t = np.reshape(t,(len(p),3)) 96 | # for i in range(len(self.p)): 97 | # seq[i][:] = seq[i] 98 | # return seq 99 | # # L= ListPos(len(p), p) 100 | # # print(L) 101 | # # class ListPos: 102 | # # def __init__(self,n=1): 103 | # # self.p_mat = np.zeros((n,3), dtype=float) 104 | # # POS = ListPos(len(p)) 105 | # # SEQ = POS.f 106 | # # p_mat = np.reshape(p, (len(p), 3)) 107 | # # print(POS.p_mat_init) 108 | 109 | 110 | -------------------------------------------------------------------------------- /instruments/smarPod/lib/FTChipID.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/instruments/smarPod/lib/FTChipID.dll -------------------------------------------------------------------------------- /instruments/smarPod/lib/MCSControl.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/instruments/smarPod/lib/MCSControl.dll -------------------------------------------------------------------------------- /instruments/smarPod/lib/SmarActIO.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/instruments/smarPod/lib/SmarActIO.dll -------------------------------------------------------------------------------- /instruments/smarPod/lib/SmarPod.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/instruments/smarPod/lib/SmarPod.dll -------------------------------------------------------------------------------- /instruments/smarPod/lib/SmarPod.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/instruments/smarPod/lib/SmarPod.lib -------------------------------------------------------------------------------- /instruments/smarPod/smaract.py: -------------------------------------------------------------------------------- 1 | 2 | # importing the libraries 3 | 4 | import ctypes 5 | from ctypes import * 6 | import sys 7 | import visa 8 | import matplotlib.pyplot as plt 9 | import numpy as np 10 | from func import* 11 | # from Smarpod_status import* 12 | 13 | from ctypes import * 14 | import numpy as np 15 | import numpy.ctypeslib as npct; 16 | from itertools import repeat; 17 | import math; 18 | import string 19 | 20 | ####################################################################################################################### 21 | # LOAD LIBRARY 22 | ####################################################################################################################### 23 | # lib1 = ctypes.WinDLL("SmarPod.dll") 24 | # print(lib1) 25 | # lib = np.ctypeslib.load_library('SmarPod.dll','.') 26 | # print("Smarpod Library .DLL :", "SmarPod.dll : ", lib) 27 | # print(lib) 28 | 29 | ####################################################################################################################### 30 | # CLASS 31 | ####################################################################################################################### 32 | 33 | # class Smapod: 34 | # def __init__(self, loc = "network:192.168.1.200:5000", lib = np.ctypeslib.load_library('SmarPod.dll', '.')): 35 | # (model, name, dll) = SmarpodInfo(lib) 36 | # # Locator adress 37 | # loc_str = loc.encode('utf-8') 38 | # loc_buffer = create_string_buffer(loc_str) 39 | # SmarpodId = c_ulong() 40 | # SmarpodId_p = pointer(SmarpodId) 41 | # locator = loc_buffer 42 | # # Open Smarpod 43 | # lib.Smarpod_Open(SmarpodId_p, model, locator, " ") 44 | # SmarpodId_val = SmarpodId.value 45 | # self.model = model 46 | # self.name = name 47 | # self.dll = dll 48 | # self.loc = loc 49 | # self.loc_buffer = loc_buffer 50 | # self.id = SmarpodId_val 51 | # self.id_p= SmarpodId 52 | 53 | # class SmapodConfigOptions: 54 | # def __init__(self, mode='Disable', freq_max=3000): 55 | # sensors_mode = {} 56 | # sensors_mode['Enable'] = "SMARPOD_SENSORS_ENABLED" 57 | # sensors_mode['Disable'] = "SMARPOD_SENSORS_DISABLED" 58 | # sensors_mode['Powersave'] = "SMARPOD_SENSORS_POWERSAVE" 59 | # self.mode = sensors_mode[mode] 60 | # self.freq_max = freq_max 61 | 62 | # class SmapodRefOptions: 63 | # Referencing < METHOD > : Smarpod_Set_ui(SmarpodId, SMARPOD_FREF_METHOD, < METHOD >) 64 | # Referencing [ DIRECTION ] : Smarpod_Set_ui(SmarpodId, [ DIRECTION ] , ( OPTION )); 65 | # - < DEFAULT > 66 | # - < METHOD_SEQUENTIAL > 67 | # - < METHOD_ZSAFE > 68 | # - [ FREF_ZDIRECTION ] 69 | # - < METHOD_XYSAFE > 70 | # - [ FREF_XDIRECTION, FREF_YDIRECTION ] 71 | # DIRECTION OPTIONS: 72 | # - ( SMARPOD_NEGATIVE, SMARPOD_POSITIVE, SMARPOD_REVERSE ) 73 | # Other properties : Smarpod_Set_ui(SmarpodId, < FREF_AND_CAL_FREQUENCY >, 8000); 74 | 75 | # def __init__(self, ref_method=0, direction=0, direction_option=0): 76 | # method = {0: "DEFAULT", 1: " METHOD_SEQUENTIAL", 2: "METHOD_ZSAFE", 3: "METHOD_XYSAFE"} 77 | # Dir = {0: " ", "z": "FREF_ZDIRECTION", "x": "FREF_XDIRECTION", "y": "FREF_YDIRECTION"} 78 | # Dir_opt = {0: " ", "-": "SMARPOD_NEGATIVE", "+": "SMARPOD_POSITIVE", "r": "SMARPOD_REVERSE"} 79 | # self.CAL_FREF_FREQUENCY = 8000 80 | # self.method = method[ref_method] 81 | # self.dir = Dir[direction] 82 | # self.dir_opt = Dir_opt[direction_option] 83 | # self.ref_max_freq = 8000 84 | 85 | # class SmapodPivotOptions: 86 | # # SET PIVOT MODE 87 | # 88 | # def __init__(self, pivot_mode='fixe', px=0, py=0, pz=0): 89 | # Pivot_mode = {} 90 | # Pivot_mode['relatif'] = 'PIVOT_RELATIVE' 91 | # Pivot_mode["fixe"] = "PIVOT_FIXED" 92 | # self.mode = Pivot_mode[pivot_mode] 93 | # self.px = px 94 | # self.py = py 95 | # self.pz = pz 96 | # Pivot=(c_double * 3)() 97 | # Pivot[0] = self.px 98 | # Pivot[1] = self.py 99 | # Pivot[2] = self.pz 100 | # self.pivot_c_double = Pivot 101 | # 102 | # # class SmarpodMove: 103 | # # def __init__(self): 104 | # # self.sp = 0.001 105 | 106 | class Smarpod_position: 107 | a = np.zeros(6) 108 | def __init__(self, X=a[0], Y=a[1], Z=a[2], OX=a[3], OY=a[4], OZ=a[5]): 109 | self.positionX = c_double(X) 110 | self.positionY = c_double(Y) 111 | self.positionZ = c_double(Z) 112 | self.rotationX = c_double(OX) 113 | self.rotationY = c_double(OY) 114 | self.rotationZ = c_double(OZ) 115 | def pose_c_double(self): 116 | pose1 = (c_double * 6)() 117 | pose1[0] = self.positionX # X 118 | pose1[1] = self.positionY # Y 119 | pose1[2] = self.positionZ # Z 120 | pose1[3] = self.rotationX # OX 121 | pose1[4] = self.rotationY # OY 122 | pose1[5] = self.rotationZ # OZ 123 | return pose1 124 | def pose_double(self): 125 | pose2 = [self.positionX.value, # X 126 | self.positionY.value, # Y 127 | self.positionZ.value, # Z 128 | self.rotationX.value, # OX 129 | self.rotationY.value, # OY 130 | self.rotationZ.value] # OZ 131 | return pose2 132 | 133 | 134 | ####################################################################################################################### 135 | # INITIALIZATION 136 | # 1. initialization of the SmarPod (see 2.4.1 Initializing a SmarPod). 137 | # 2. activation of the sensors operation mode (see 2.2 Sensor Modes). 138 | # 3. configuration of the SmarPod (2.4.3 Configuring the SmarPod). 139 | # 4. (if necessary) calibration of the positioner sensors (see 2.4.4 Calibrating the Sensors). 140 | # 5. (if necessary) finding the positioner's reference marks (see 2.4.5 Finding Reference Marks). 141 | ####################################################################################################################### 142 | 143 | # print(" %%%%%%%%%%%%%%%% \n %INITIALIZATION% \n %%%%%%%%%%%%%%%%") 144 | # 145 | # SP = Smapod("network:192.168.1.200:5000") 146 | # config = SmapodConfigOptions() 147 | # ref = SmapodRefOptions() 148 | # set_pivot =SmapodPivotOptions() 149 | # 150 | # 151 | # # 1. Initialization 152 | # # SmarPod info 153 | # # Open Smarpod 154 | # status = lib.Smarpod_Open(SP.id_p, SP.model, SP.loc_buffer, " ") 155 | # # Get id 156 | # SmarpodId = SP.id 157 | # if statuscode(status) == "SMARPOD_OK" : 158 | # print("Smarpod_Open : ", "Smarpod_Status : ", statuscode(status), "<", (status), ">") 159 | # else: 160 | # print("Initialization OK \n Next step is : Sensors activation") 161 | # 162 | # # 2.Sensors activation 163 | # config.mode = 'Enable' 164 | # # Mode selection: 165 | # Smarpod_SetSensorMode = lib.Smarpod_SetSensorMode 166 | # status = Smarpod_SetSensorMode(SmarpodId, config.mode) 167 | # if statuscode(status) == "SMARPOD_OK": 168 | # print("Smarpod_SetSensorMode :","Smapod_Status : ", statuscode(status),"<",status,">") 169 | # else: 170 | # print("Sensors activation OK \n Next step is : Configuration") 171 | # 172 | # # 3. Configuration 173 | # # - Smarpod_SetSensorMode -OK 174 | # # - Smarpod_SetMaxFrequency (1 to 18500) 175 | # freq_max = 3000 176 | # Smarpod_SetMaxFrequency = lib.Smarpod_SetMaxFrequency 177 | # status = Smarpod_SetMaxFrequency(SmarpodId, freq_max) 178 | # if statuscode(status) == "SMARPOD_OK": # must be == 179 | # print("Smarpod_SetMaxFrequency :","Smapod_Status : ", statuscode(status),"<",status,">") 180 | # else: 181 | # # - Smarpod_SetSpeed 182 | # # Select speed control 183 | # speed_control = 1 # 1 = enable ou 0 = disable speed-control 184 | # # Select speed (max reachable speed is typically 1 to 5 mm/s) 185 | # speed = 0.001 186 | # Smarpod_SetSpeed = lib.Smarpod_SetSpeed 187 | # status = Smarpod_SetSpeed(SmarpodId, speed_control, c_double(speed)) 188 | # if statuscode(status) == "SMARPOD_OK": # must be != 189 | # print("Smarpod_SetSpeed :", "Smapod_Status : ", statuscode(status), "<", status, ">") 190 | # else: 191 | # print("Configuration OK \n Next step is : Calibration (if necessary)") 192 | # 193 | # # 4. Calibration (if necessary) 194 | # calibrate = 0 # 0 = dont want calibration, 1= want calibration 195 | # if statuscode(status) != "SMARPOD_OK": # must be == 196 | # if calibrate == 1: 197 | # Smarpod_Calibrate = lib.Smarpod_Calibrate 198 | # status = Smarpod_Calibrate(SmarpodId) 199 | # if statuscode(status) != "SMARPOD_OK": 200 | # print("Smarpod_Calibrate :", "Smapod_Status : ", statuscode(status), "<", status, ">") 201 | # elif calibrate != 0: 202 | # print("Select 1 to calibrate or 0 to continue ") 203 | # else: 204 | # print("Calibration OK \n Next step is : Finding reference marks (if necessary)") 205 | # else: 206 | # print("Calibration : ", statuscode(status)) 207 | # 208 | # # 5. Finding reference marks (if necessary) 209 | # referenced = c_int() 210 | # referenced_p = pointer(referenced) 211 | # 212 | # lib.Smarpod_Set_ui(SmarpodId, "SMARPOD_FREF_METHOD", ref.method) 213 | # lib.Smarpod_Set_ui(SmarpodId, "FREF_AND_CAL_FREQUENCY", ref.CAL_FREF_FREQUENCY) 214 | # status = lib.Smarpod_IsReferenced(SmarpodId, referenced_p) 215 | # if statuscode(status) != "SMARPOD_OK": # must be == 216 | # if referenced.value == 0: 217 | # status = lib.Smarpod_FindReferenceMarks(SmarpodId) 218 | # if statuscode(status) != "SMARPOD_OK": # must be == 219 | # print("Referencing OK : \n Initialization completed") 220 | # else: 221 | # print("Referencing : ", statuscode(status)) 222 | # else: 223 | # print("Referencing OK : \n Initialization completed") 224 | # else: 225 | # print("Referencing : ", statuscode(status)) 226 | # 227 | # # PIVOT 228 | # print("Pivot calibration : ") 229 | # lib.Smarpod_Set_ui(SmarpodId, "PIVOT_MODE","PIVOT_RELATIVE" ) 230 | # # SET PIVOT POSITION 231 | # pivot = (c_double *3)() 232 | # pivot[0] = 0 # PX 233 | # pivot[1] = 0 # PY 234 | # pivot[2] = 0.001 # PZ 235 | # status = lib.Smarpod_SetPivot(SmarpodId, pivot) 236 | # print(" ", statuscode(status)) 237 | 238 | # MOVE 239 | # SET position 240 | ######################################################################################################################## 241 | 242 | # Position initiale trouve manuellement 243 | xinit, yinit,zinit = 0,0,0 244 | oxinit,oyinit,ozinit = 0,0,0 245 | 246 | # Position Y Max (security) 247 | Ymax_security = 1 248 | 249 | # Incrementation 250 | dx = 0.5; dox = 0.5 251 | dy = 0.5; doy = 0.5 252 | dz = 0.5; doz = 0.5 253 | 254 | # Span 255 | xspan, yspan, zspan = 2, 1, 1 256 | oxspan, oyspan, ozspan = 1, 2, 3 257 | 258 | # Sequence balayage XYZ (Angles fixes) 259 | p1 = MakeSeq(xinit, yinit, zinit, oxinit, oyinit, ozinit, xspan, yspan, ozspan, oxspan, oyspan, zspan, dx, dy, dz) 260 | print(p1[:]) 261 | 262 | # for i in range(len(p1)): 263 | # SP = Smarpod_position(p1[i][0],p1[i][1],p1[i][2],p1[i][3],p1[i][4],p1[i][5]) 264 | # pose = SP.pose_c_double() 265 | # result = lib.Smarpod_Move(SmarpodId,pose,"SMARPOD_HOLDTIME_INFINITE", 1) 266 | # # print(result) 267 | # 268 | # 269 | # 270 | # Minor = c_int32() 271 | # Major = c_int32() 272 | # Update = c_int32() 273 | # # minor = pointer(Minor) 274 | # # major = pointer(Major) 275 | # # update = pointer(Update) 276 | # Smarpod_GetDLLVersion = lib.Smarpod_GetDLLVersion 277 | # Smarpod_GetDLLVersion.argtypes = [POINTER(c_int32), POINTER(c_int32), POINTER(c_int32)] 278 | # Smarpod_GetDLLVersion.restype = c_uint32 279 | # Smarpod_GetDLLVersion(Major, Minor, Update) 280 | # 281 | # # print("Smarpod_GetDLLVersion : ", Major.value, ".", Minor.value, ".", Update.value) 282 | # # 283 | # # class smarpodcclasstest(object): 284 | # # def __init__(self): 285 | # # self.lib = np.ctypeslib.load_library('SmarPod.dll','.') 286 | # # self.loc = "network:192.168.1.200:5000" 287 | # # # self.createPrototypes() 288 | # # self.connected = False 289 | # # # def createPrototypes(self): 290 | # # # Minor = c_int32() 291 | # # # Major = c_int32() 292 | # # # Update = c_int32() 293 | # # # minor = pointer(Minor) 294 | # # # major = pointer(Major) 295 | # # # update = pointer(Update) 296 | # # Smarpod_GetDLLVersion = self.lib.Smarpod_GetDLLVersion 297 | # # self.Smarpod_GetDLLVersion = Smarpod_GetDLLVersion 298 | # # # self.Smarpod_GetDLLVersion.argtypes = [POINTER(c_int32), POINTER(c_int32), POINTER(c_int32)] 299 | # # # self.Smarpod_GetDLLVersion.restype = POINTER() #(major, minor, update) 300 | # # # print("Smarpod_GetDLLVersion : ", Major.value, ".", Minor.value, ".", Update.value) 301 | # # 302 | # # c = smarpodcclasstest.Smarpod_GetDLLVersion 303 | # # print(lib.eggs('Smarpod_Status')) 304 | # # lib.SMARPOD_SENSORS_ENABLED 305 | # # info = (c_char_p*1)() 306 | # # info_p = pointer(info) 307 | # # print(lib.Smarpod_GetStatusInfo(0,info_p)) 308 | # # print(info[0]) 309 | # # class Pose(Structure): 310 | # # _fields_ = [("positionX", c_double), 311 | # # ("positionY", c_double), 312 | # # ("positionZ", c_double), 313 | # # ("rotationX", c_double), 314 | # # ("rotationZ", c_double), 315 | # # ("rotationZ", c_double) 316 | # # ] 317 | # # pose_p = POINTER(Pose) 318 | # # pp = Pose() 319 | # # pose5 = (c_double*2)() 320 | # # # pose5[0] = c_double(1) 321 | # # # pose5[1] = c_double() 322 | # # r =lib.Smarpod_GetPose(SmarpodId, pp) 323 | # print(r) 324 | # print(pose5[0]) 325 | # pose5.positionx = pose[0] 326 | # print(pose5.positionx) 327 | # print(pp[0]) 328 | # # print(pp._fields_[0][1]) 329 | # 330 | # # pose = (c_double * 6)() 331 | # class Pose(Structure): 332 | # _fields_ = [("positionX", c_double), 333 | # ("positionY", c_double), 334 | # ("positionZ", c_double), 335 | # ("rotationX", c_double), 336 | # ("rotationY", c_double), 337 | # ("rotationZ", c_double) 338 | # ] 339 | # # positionx = pose[0] 340 | # # positiony = pose[1] 341 | # # positionz = pose[2] 342 | # # rotationx = pose[3] 343 | # # rotationy = pose[4] 344 | # # rotationz = pose[5] 345 | # pose = Pose() 346 | # pose.positionX= 7 347 | # pose.positionY = 5 348 | # pose.positionZ = 3 349 | # pose.rotationX = 1 350 | # pose.rotationY = 2 351 | # pose.rotationZ = 2 352 | # # position_array = [pose[0], pose[1], pose[2], pose[3], pose[4], pose[5]] 353 | # print(pose.) -------------------------------------------------------------------------------- /instruments/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Miscellaneous methods regarding the remote control of instruments in the Lab. 3 | 4 | Author: Simon Belanger-de Villers 5 | Date: November 19th 2019 6 | """ 7 | 8 | import visa 9 | 10 | def checkRessources(): 11 | " List the ressources currently connected to the computer." 12 | rm = visa.ResourceManager() 13 | 14 | for i in rm.list_resources(): 15 | try: 16 | x = rm.open_resource(i) 17 | print(i + '\n' + x.query('*IDN?') + '\n') 18 | except visa.VisaIOError: 19 | print(i + '\nInstrument Not found\n') 20 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | General all-purpose script 3 | 4 | Author : Simon Belanger-de Villers 5 | Date created : 13 November 2019 6 | Last edited : 3 December 2019 7 | """ 8 | from Instruments.Keithley_2612B import Keithley_2612B 9 | from Instruments.Agilent_E3631A import Agilent_E3631A 10 | from Instruments.hp816x_instr_py3 import hp816x 11 | from Instruments.utils import * 12 | from dev.MRF_FBL.MRF import RealMRF 13 | from dev.MRF_FBL.Algo import * 14 | import matplotlib.pyplot as plt 15 | plt.switch_backend('TkAgg') 16 | import numpy as np 17 | import time, os 18 | from AddDropOVA import * 19 | from IVcurves import * 20 | from data.sweepobj import sweepobj 21 | from utils import * 22 | 23 | print('code start...\n') 24 | scanStartTime = time.time() 25 | 26 | # Agilent LightWave Measurement System 27 | LMS = hp816x() 28 | 29 | # Connect to the DC Sources 30 | V1 = Keithley_2612B(0, 24,'a') 31 | V2 = Keithley_2612B(0, 24,'b') 32 | V3 = Keithley_2612B(0, 26,'a') 33 | V4 = Keithley_2612B(0, 26,'b') 34 | V5 = Agilent_E3631A(0, 6, 25) 35 | V1.source_range_voltage(20) 36 | V2.source_range_voltage(20) 37 | V3.source_range_voltage(20) 38 | V4.source_range_voltage(20) 39 | 40 | # Init System 41 | instruments = {'LMS': LMS, 42 | 'dropChan': (4,0), 43 | 'thruChan': (1,0), 44 | 'DCsources': [V1, V2, V3, V4, V5], 45 | 'LowerLimitDC': [0]*5, 46 | 'UpperLimitDC': [3]*5} 47 | data_dir = os.getcwd() + '\\measures\\FilterTuning\\die03\\' 48 | mrf = RealMRF(instruments, 0.01, data_dir) 49 | 50 | # Check if heaters have a trouble# if the optical alignment is good 51 | #acquireIVCurveMultiple([V1, V2, V3, V4, V5], np.linspace(0, 3, 40), None) 52 | #mrf.wavelengthSweep(1565e-9, 1572e-9, 0.012e-9, True, True, None) 53 | 54 | #mrf.apply_bias(1, 2) 55 | #mrf.wavelengthSweep(1500e-9, 1570e-9, 0.06e-9,False, True, None) 56 | 57 | #wavelength = np.linspace(1500e-9, 1600e-9, 1000) 58 | #mrf.LMS.setTLSPower(0) 59 | #mrf.LMS.setTLSState('on') 60 | #power = [] 61 | #for wvl in wavelength: 62 | # mrf.LMS.setTLSWavelength(wvl) 63 | # power.append(mrf.LMS.readPWM(4, 0)) 64 | #plt.plot(wavelength, power) 65 | #plt.show() 66 | #from data.sweepobj import sweepobj 67 | #swobj = sweepobj() 68 | #swobj.wavelength = wavelength 69 | #swobj.detector_1 = power 70 | #data_dir = os.getcwd() + '\\measures\\laserPowerCalib\\' 71 | #swobj.save(data_dir + 'laserCurve') 72 | 73 | maxPow1500_1600 = mrf.LMS.getTLSMaxPower(1500e-9, 1600e-9) 74 | #maxPow1525_1575 = mrf.LMS.getTLSMaxPower(1525e-9, 1575e-9) 75 | #maxPow1540_1560 = mrf.LMS.getTLSMaxPower(1540e-9, 1560e-9) 76 | 77 | 78 | mrf.wavelengthSweep(maxPow1500_1600, 1500e-9, 1600e-9, 0.012e-9, True, True, None) 79 | #mrf.wavelengthSweep(maxPow1500_1600, 1525e-9, 1575e-9, 0.012e-9,False, True, None) 80 | #mrf.wavelengthSweep(maxPow1500_1600, 1540e-9, 1560e-9, 0.012e-9,False, True, None) 81 | 82 | 83 | 84 | # End of code 85 | scanStopTime = time.time() 86 | print('code done in {0:0.3f}.'.format(scanStopTime-scanStartTime)) -------------------------------------------------------------------------------- /manuals/Agilent_81536A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_81536A.pdf -------------------------------------------------------------------------------- /manuals/Agilent_81600B.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_81600B.pdf -------------------------------------------------------------------------------- /manuals/Agilent_81640B.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_81640B.pdf -------------------------------------------------------------------------------- /manuals/Agilent_816x_PG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_816x_PG.pdf -------------------------------------------------------------------------------- /manuals/Agilent_816x_UG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_816x_UG.pdf -------------------------------------------------------------------------------- /manuals/Agilent_E3631A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_E3631A.pdf -------------------------------------------------------------------------------- /manuals/Agilent_E3646A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Agilent_E3646A.pdf -------------------------------------------------------------------------------- /manuals/Anritsu_MS9740A_PG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Anritsu_MS9740A_PG.pdf -------------------------------------------------------------------------------- /manuals/Anritsu_MS9740A_UG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Anritsu_MS9740A_UG.pdf -------------------------------------------------------------------------------- /manuals/EXFO_PM-1600_PG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/EXFO_PM-1600_PG.pdf -------------------------------------------------------------------------------- /manuals/EXFO_T100S-HP_PG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/EXFO_T100S-HP_PG.pdf -------------------------------------------------------------------------------- /manuals/EXFO_T100S-HP_UG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/EXFO_T100S-HP_UG.pdf -------------------------------------------------------------------------------- /manuals/ILX_LDT-5910B.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/ILX_LDT-5910B.pdf -------------------------------------------------------------------------------- /manuals/JDS_SBOSwitch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/JDS_SBOSwitch.pdf -------------------------------------------------------------------------------- /manuals/Keithley_2612B.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Keithley_2612B.pdf -------------------------------------------------------------------------------- /manuals/Luna_OVA5000.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/Luna_OVA5000.pdf -------------------------------------------------------------------------------- /manuals/SHF_S807.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/manuals/SHF_S807.pdf -------------------------------------------------------------------------------- /misc/WavelengthSweepTemplateNotebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Wavelength Sweep Template Notebook\n", 8 | "\n", 9 | "This Jupyter notebook contains the code used to perform a wavelength sweep using the agilent Tunable Laser Source (TLS). You can use this template and copy/paste it's content in your own notebook in order to perform sweeps.\n", 10 | "\n", 11 | "Author: Simon Belanger-de Villers\n", 12 | "Date: November 5th 2019" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "from Instruments import hp816x_instr\n", 22 | "from data.sweepobj import sweepobj\n", 23 | "from data.wvlsweep import wvlsweep\n", 24 | "import os\n", 25 | "%matplotlib inline\n", 26 | "\n", 27 | "data_dir = os.getcwd() + \"\\\\...\\\\\"\n", 28 | "LMS = hp816x_instr.hp816x()\n", 29 | "LMS.connect('GPIB0::20::INSTR')\n", 30 | "print(\"Saving wavelength sweep to \" + data_dir + \" ...\")\n", 31 | "wvlsweep(LMS, data_dir, wvl_start=1530e-9, wvl_stop=1570e-9, wvl_step=0.01e-9, plot_det1 = True, plot_det2 = True, filename=\"test\")\n", 32 | "LMS.setTLSState(\"off\")" 33 | ] 34 | } 35 | ], 36 | "metadata": { 37 | "kernelspec": { 38 | "display_name": "Python 3", 39 | "language": "python", 40 | "name": "python3" 41 | }, 42 | "language_info": { 43 | "codemirror_mode": { 44 | "name": "ipython", 45 | "version": 3 46 | }, 47 | "file_extension": ".py", 48 | "mimetype": "text/x-python", 49 | "name": "python", 50 | "nbconvert_exporter": "python", 51 | "pygments_lexer": "ipython3", 52 | "version": "3.6.5" 53 | } 54 | }, 55 | "nbformat": 4, 56 | "nbformat_minor": 1 57 | } 58 | -------------------------------------------------------------------------------- /misc/agilent_control.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Commands to control the Agilent laser\n", 8 | "\n", 9 | "Import the library to access remote control.\n", 10 | "\n", 11 | "TODO : Start from pyOptoMip and make a custom gui to save sweeps as pickle files." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 11, 17 | "metadata": { 18 | "collapsed": false 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "import hp816x_instr" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "Connect to the mainframe" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 21, 35 | "metadata": { 36 | "collapsed": false 37 | }, 38 | "outputs": [ 39 | { 40 | "name": "stdout", 41 | "output_type": "stream", 42 | "text": [ 43 | "Disconnected from the laser\n", 44 | "The mainframe is: HP8164A\n", 45 | "Connected to the laser\n" 46 | ] 47 | } 48 | ], 49 | "source": [ 50 | "LMS = hp816x_instr.hp816x()\n", 51 | "LMS.connect('GPIB0::20::INSTR')" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "Select which laser output to use." 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 22, 64 | "metadata": { 65 | "collapsed": true 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "LMS.setTLSOutput('lowsse')\n", 70 | "#LMS.setTLSOutput('highpower')" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "Turn the laser output on or off." 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 31, 83 | "metadata": { 84 | "collapsed": false 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "LMS.setTLSState(\"on\")\n", 89 | "#LMS.setTLSState(\"off\")" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "Select the laser wavelength." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": { 103 | "collapsed": true 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "LMS.setTLSWavelength(1550e-9)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "Select the laser power." 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 26, 120 | "metadata": { 121 | "collapsed": true 122 | }, 123 | "outputs": [], 124 | "source": [ 125 | "LMS.setTLSPower(-6)" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "Disconnect from the mainframe." 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 9, 138 | "metadata": { 139 | "collapsed": false 140 | }, 141 | "outputs": [ 142 | { 143 | "name": "stdout", 144 | "output_type": "stream", 145 | "text": [ 146 | "Disconnected from the laser\n" 147 | ] 148 | } 149 | ], 150 | "source": [ 151 | "LMS.disconnect()" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "metadata": { 158 | "collapsed": true 159 | }, 160 | "outputs": [], 161 | "source": [] 162 | } 163 | ], 164 | "metadata": { 165 | "kernelspec": { 166 | "display_name": "Python 2", 167 | "language": "python", 168 | "name": "python2" 169 | }, 170 | "language_info": { 171 | "codemirror_mode": { 172 | "name": "ipython", 173 | "version": 2 174 | }, 175 | "file_extension": ".py", 176 | "mimetype": "text/x-python", 177 | "name": "python", 178 | "nbconvert_exporter": "python", 179 | "pygments_lexer": "ipython2", 180 | "version": "2.7.11" 181 | } 182 | }, 183 | "nbformat": 4, 184 | "nbformat_minor": 0 185 | } 186 | -------------------------------------------------------------------------------- /misc/testqontrol.py: -------------------------------------------------------------------------------- 1 | """ 2 | This Jupyter-like file allows to pass commands interactively to the 3 | Qontrol System using vsCode. (https://code.visualstudio.com/docs/python/jupyter-support) 4 | 5 | Run the different code cells interactively in order to send commands to the device. 6 | 7 | Author : Simon Bélanger-de Villers 8 | Created : July 28th 2019 9 | Last Edited : July 31st 2019 10 | """ 11 | 12 | 13 | #%% Initialize the Qontrol 14 | def listSerialPorts(): 15 | " List all serial ports on this computer. " 16 | 17 | from serial.tools.list_ports import comports 18 | 19 | for ports in comports(): 20 | print(ports.device) 21 | 22 | #listSerialPorts() 23 | 24 | # NOTE : Jupyter Server's current working directory is in ULPythonLab 25 | from Instruments.qontrol import QXOutput 26 | #from qontrol import QXOutput 27 | import time 28 | q = QXOutput(serial_port_name = "/dev/cu.usbserial-FT2ZGKOF", response_timeout = 0.1) 29 | 30 | #%% Low level Write Command 31 | 32 | q.transmit('V16 = 1.'+'\n') 33 | 34 | #%% Low level query command 35 | 36 | q.transmit('ID?'+'\n') # Identity of the driver @ Slot 0 37 | q.transmit('NCHAN?'+'\n') # Number of channels of the driver @ Slot 0 38 | q.transmit('NUPALL?'+'\n') # Information on the daisychain 39 | message = q.receive()[0] 40 | for line in message: 41 | print(line) 42 | 43 | #%% 44 | print(q.chain) 45 | 46 | #%% Very low level command 47 | command_string = 'V0 = 0.'+'\n' 48 | q.serial_port.write(command_string.encode('ascii')) 49 | 50 | #%% Check if binary solves the issue 51 | 52 | q.binary_mode = True 53 | 54 | q.v[:] = 1 55 | 56 | #%% Do Stuff 57 | 58 | q.v[16] = 2 59 | #q.i[] = 1 60 | 61 | print(q.i[16]) 62 | 63 | #%% Set voltage limit 64 | 65 | q.vmax[:] = q.v_full 66 | print(q.vmax) 67 | 68 | #%% Turn off all channels 69 | q.i[:] = 0 70 | q.v[:] = 0 71 | #%% Close the serial port 72 | q.close() 73 | 74 | #%% 75 | -------------------------------------------------------------------------------- /utilitaries/AddDropOVA.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions used to characterise both the drop and through port of an add drop filter using the OVA + can optical switch. 3 | 4 | Author: Simon Belanger-de Villers 5 | Date created: 20 November 2019 6 | Last edited: 20 November 2019 7 | """ 8 | import matplotlib.pyplot as plt 9 | from data.sweepobj import sweepobj 10 | import numpy as np 11 | import time 12 | import os 13 | import pickle 14 | 15 | def measureFilterTransmission(switch, luna): 16 | '''Measure the Thru/Drop port transmission spectrum.''' 17 | 18 | # Dicts with parameters 19 | osPorts = {'Thru': 2, 'Drop': 1} # Optical switch ports 20 | dutLength = {'Thru': 28.162, 'Drop': 22.557} # DUT Length of the scans 21 | 22 | # Measure the thru port spectrum 23 | switch.setChannel(osPorts['Thru']) 24 | luna.dutLength = dutLength['Thru'] 25 | luna.scan() 26 | thru = luna.fetchResult('0') 27 | 28 | # Measure the drop port spectrum 29 | switch.setChannel(osPorts['Drop']) 30 | luna.dutLength = dutLength['Drop'] 31 | luna.scan() 32 | drop = luna.fetchResult('0') 33 | 34 | # Fetch the X axis data 35 | wvl = luna.fetchXAxis() 36 | 37 | return wvl, thru, drop 38 | 39 | def plotTransmission(wvl, thru, drop): 40 | '''Plot the Thru/Drop transmission spectrum.''' 41 | 42 | plt.plot(wvl, thru, color='b', label='Through Port') 43 | plt.plot(wvl, drop, color='r', label='Drop Port') 44 | plt.xlabel('Wavelength [nm]'), plt.ylabel('Transmission [dB]') 45 | plt.title('OVA Scan') 46 | plt.xlim([min(wvl), max(wvl)]), plt.ylim([-70, -10]) 47 | plt.grid(), plt.legend(), plt.show() 48 | 49 | def saveTransmission(wvl, thru, drop, filename): 50 | '''Save the spectrum in a pickle file.''' 51 | 52 | swobj = sweepobj() 53 | swobj.filename = "" 54 | swobj.device = "" 55 | swobj.info = "" 56 | swobj.wavelength_start = min(wvl) 57 | swobj.wavelength_stop = max(wvl) 58 | swobj.wavelength_step = (max(wvl)-min(wvl))/len(wvl) 59 | swobj.xlims = [min(wvl)*1e9, max(wvl)*1e9] # Defaults 60 | swobj.ylims = [-100, 0] # Defaults 61 | 62 | # Put the data 63 | swobj.wavelength = np.asarray(wvl, dtype=object) 64 | swobj.detector_1 = np.asarray(thru, dtype=object) 65 | swobj.detector_2 = np.asarray(drop, dtype=object) 66 | 67 | # Save the datafile 68 | swobj.save(filename) 69 | print("Saving the data to " + filename + " ...") 70 | 71 | def checkParams(): 72 | ''' Measure the actual central wavelength vs different programmed central wavelengths.''' 73 | luna.rangeWav = 0.63 74 | target, meas = [],[] 75 | for ran in np.linspace(1500, 1600, 100): 76 | luna.centerWav = ran 77 | target.append(ran) 78 | meas.append(luna.centerWav) 79 | 80 | plt.plot(target, meas) 81 | plt.show() 82 | 83 | if __name__ == '__main__': 84 | 85 | # Connect to the Optical Switch 86 | switch = JDSopticalSwitch(0, 7) 87 | switch.connect() 88 | 89 | # Connect to the OVA 90 | luna = Luna("10.9.32.234", 8888) 91 | 92 | # Perform a scan using the OVA 93 | luna.rangeWav = 5 94 | luna.centerWav = 1553 95 | luna.disableAverage() 96 | 97 | wvl, thru, drop = measureFilterTransmission(switch, luna) 98 | plotTransmission(wvl, thru, drop) 99 | filename = os.getcwd() + '//measures//die11_v2_zoom' 100 | saveTransmission(wvl, thru, drop, filename) 101 | sweepobj(filename).show() 102 | luna.close() -------------------------------------------------------------------------------- /utilitaries/IVcurves.py: -------------------------------------------------------------------------------- 1 | """ 2 | Perform IV curves testing in python and save the results. 3 | 4 | Author: Simon Belanger-de Villers 5 | Date created: 25 November 2019 6 | Last edited: 26 November 2019 7 | """ 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | import os, pickle 11 | 12 | def measureIVCurve(powerSupply, voltage, doPlot=False): 13 | '''Measure and plot the IV curve of a given heater/actuator.''' 14 | current = [] 15 | powerSupply.source_voltage(0) 16 | powerSupply.output_on() 17 | for v in voltage: 18 | powerSupply.source_voltage(v) 19 | current.append(powerSupply.measure_current()) 20 | if doPlot: 21 | plt.plot(voltage, np.asarray(current)*1e3, label=powerSupply.name) 22 | plt.xlabel("Voltage [V]"), plt.ylabel("Current [mA]"), plt.legend(), plt.grid(), plt.show() 23 | powerSupply.output_off() 24 | return np.asarray(voltage), np.asarray(current) 25 | 26 | def acquireIVCurveMultiple(powerSuppliesList, voltage, filename=None): 27 | '''Measure the current, resistance and applied power vs voltage and plot everything in the same plot.''' 28 | 29 | V, IAll, labels = measureMultipleIVCurve(powerSuppliesList, voltage) 30 | plotMultipleIVCurve(V, IAll, labels) 31 | 32 | if filename != None: 33 | # check if the file exists 34 | if os.path.exists(filename): 35 | decision = input('Warning: The file already exists. Press x to overwrite...\n') 36 | if decision != 'x': 37 | print('The file will not be saved.') 38 | exit() 39 | 40 | # Save the file 41 | print('saving file as ' + filename + '\n') 42 | outfile = open(filename,'wb') 43 | data = {'voltage [V]': V, 'current [A]': IAll, 'labels': labels} 44 | pickle.dump(data, outfile) 45 | outfile.close() 46 | 47 | def measureMultipleIVCurve(powerSuppliesList, voltage): 48 | ''' measure the IV curve for all heaters.''' 49 | 50 | IAll = [] 51 | labels = [] 52 | for powerSupply in powerSuppliesList: 53 | V, I = measureIVCurve(powerSupply, voltage) 54 | IAll.append(I) 55 | labels.append(powerSupply.name) 56 | return V, IAll, labels 57 | 58 | def plotMultipleIVCurve(V, IAll, labels, savePDF=True): 59 | '''Plot the IV curve for all heaters.''' 60 | 61 | figure = plt.figure(figsize=(15,7.5)) 62 | IVplot = figure.add_subplot(3,1,1) 63 | plt.ylabel("Current [mA]"), plt.grid() 64 | Rplot = figure.add_subplot(3,1,2, sharex=IVplot) 65 | plt.ylabel("Resistance [ohms]"), plt.grid(), plt.ylim([100, 200]) 66 | Pplot = figure.add_subplot(3,1,3, sharex=IVplot) 67 | plt.xlabel("Voltage [V]"), plt.ylabel("Power [mW]"), plt.grid() 68 | for I, label in zip(IAll, labels): 69 | IVplot.plot(V, I*1e3, label=label) 70 | Rplot.plot(V, V/I, label=label) 71 | Pplot.plot(V, I*V*1e3, label=label) 72 | handles, labels = Pplot.get_legend_handles_labels() 73 | figure.legend(handles, labels, loc='upper right') 74 | plt.show() 75 | if savePDF==True: 76 | figure.savefig(input('Enter a name for the pdf (without the extension):\n')+'.pdf', bbox_inches='tight') 77 | 78 | def loadIVCurveMultiple(filename): 79 | ''' Open a file containing an IV curve and plot it.''' 80 | infile = open(filename,'rb') 81 | data = pickle.load(infile) 82 | infile.close() 83 | 84 | plotMultipleIVCurve(data['voltage [V]'], data['current [A]'], data['labels']) 85 | 86 | def analyzeBreadownIVCurve(filename, treshold = 0.015): 87 | ''' Open a file containing a breadown test and extract the breakdown current, the breakdown power 88 | and the average resistance of the heater.''' 89 | 90 | # Open the file 91 | infile = open(filename,'rb') 92 | data = pickle.load(infile) 93 | infile.close() 94 | 95 | # Extract the breakdown current for each curve 96 | for I in data['current [A]']: # for each dataset 97 | previous = 0 98 | print('Breakdown current = \t{:0.3f} mA'.format(np.max(I)*1e3)) 99 | P = I * data['voltage [V]'] 100 | index = I.tolist().index(np.max(I)) 101 | print('Breakdown power = \t{:0.3f} mW'.format(1e3*P[index])) 102 | plt.show() 103 | 104 | if __name__ == '__main__': 105 | 106 | # Set the voltage that will be swept 107 | voltage = np.linspace(0, 4, 40) 108 | 109 | # Name the output file 110 | import os 111 | filename = os.getcwd() + '\\measures\\ElectricalCharacterisation\\die03\\SC_DP_GC_v1_breakdown.pickle' 112 | 113 | # Acquire the data 114 | V1 = Keithley_2612B(0, 24,'a') 115 | V2 = Keithley_2612B(0, 24,'b') 116 | acquireIVCurveMultiple([V1, V2], voltage, filename) 117 | 118 | # Load and plot the data 119 | loadIVCurveMultiple(filename) 120 | 121 | # Analyze the data 122 | analyzeBreadownIVCurve(filename) -------------------------------------------------------------------------------- /utilitaries/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simon-Belanger/ULPythonLab/7239dc885fb51b51764ea09365ac6c78376b5386/utilitaries/__init__.py -------------------------------------------------------------------------------- /utilitaries/utils.py: -------------------------------------------------------------------------------- 1 | from Instruments.hp816x_instr_py3 import hp816x 2 | import sys 3 | from tkinter import * 4 | import time 5 | import matplotlib.pyplot as plt 6 | from data.wvlsweep import wvlsweep 7 | 8 | def dummyPowerGauge(): 9 | """ Show the power measured on the detector in order to perform optical alignment. """ 10 | 11 | # Initialize the laser 12 | LMS = hp816x() 13 | LMS.connect('GPIB0::20::INSTR') 14 | LMS.setTLSOutput('lowsse') 15 | LMS.setTLSWavelength(1568e-9) 16 | LMS.setTLSPower(0) 17 | LMS.setPWMAveragingTime(1, 0, 1) 18 | LMS.setPWMAveragingTime(4, 0, 1) 19 | LMS.setPWMPowerUnit(1, 0, 'dBm') 20 | LMS.setPWMPowerUnit(4, 0, 'dBm') 21 | LMS.setPWMPowerRange(1, 0, 'manual', 0) 22 | LMS.setPWMPowerRange(4, 0, 'manual', 0) 23 | LMS.setTLSState('on') 24 | 25 | # Setup the GUI 26 | root = Tk() 27 | l1 = Label(root) 28 | l1.pack() 29 | l2 = Label(root) 30 | l2.pack() 31 | 32 | def clock(): 33 | l1.config(text='P {0:0.4f} dBm'.format(LMS.readPWM(1, 0))) 34 | l2.config(text='P {0:0.4f} dBm'.format(LMS.readPWM(4, 0))) 35 | root.after(1000, clock) 36 | 37 | # run first time 38 | clock() 39 | root.mainloop() 40 | 41 | def measureAlignmentThermalDrift(mrf): 42 | """ Measure the time response of the thermal drift of the optical alignment. """ 43 | 44 | # Turn on the laser 45 | mrf.activePort = 'Drop' 46 | mrf.LMS.setTLSWavelength(1568e-9) 47 | mrf.LMS.setTLSPower(0) 48 | mrf.LMS.setTLSState('on') 49 | time.sleep(2) 50 | 51 | # loop 52 | loopStartTime = time.time() 53 | timer, power = [],[] 54 | for iter in range(3000): 55 | timer.append(time.time()-loopStartTime) 56 | time.sleep(0.1) 57 | #measure the current every second 58 | power.append(mrf.measurePower()) 59 | 60 | if iter==100: # Turn on the voltage 61 | mrf.apply_bias(1, 2) 62 | if iter==1500: # Turn off the voltage 63 | mrf.apply_bias(1, 0) 64 | 65 | # Plot the drift 66 | plt.plot(timer, power) 67 | plt.xlabel('Time [s]') 68 | plt.ylabel('Power [dB]') 69 | plt.grid() 70 | plt.show() 71 | 72 | if __name__ == '__main__': 73 | dummyPowerGauge() --------------------------------------------------------------------------------