2024-10-30 11:50:26 +00:00
# -*- coding: utf-8 -*-
"""
Created on 29.10 .2024 , 09 : 00 hrs
LC Controller measurement script
@author : Serdar , adjusted by Lukas and Ryan
"""
############################################
# Packages from Ryan
import re
import math
import threading
import pyvisa
import time
# from pyvisa import ResourceManager, constants
# NOTE: KLCCommandLib64.py must be in the same folder as this script!
try :
from KLCCommandLib64 import *
except OSError as ex :
print ( " Warning: " , ex )
2024-10-31 10:33:22 +00:00
# Maximum output voltage
2024-10-31 09:16:31 +00:00
V_MAX = 25 # corresponds to the max RMS voltage of 25 V of KLC101 device
2024-10-31 10:33:22 +00:00
# NOTE: AC output of KLC101 is a square wave, RMS = Amplitude
2024-10-30 11:50:26 +00:00
############################################
2024-10-31 12:01:37 +00:00
# import AMC # TODO: removed, this package only relevant for the positioner in the 1000 cryostat
2024-10-30 11:50:26 +00:00
import csv
import time
import clr
import sys
2024-10-31 09:16:31 +00:00
import os , glob , string
2024-10-30 11:50:26 +00:00
import spe2py as spe
import spe_loader as sl
import pandas as pd
import time
from System . IO import *
from System import String
import numpy as np
import matplotlib . pyplot as plt
import datetime
from typing import Union
# NOTE: this is possibly not needed, remove later
#First choose your controller
IP_AMC300 = " 192.168.1.1 "
IP_AMC100 = " 192.168.71.100 "
# IP = "192.168.1.1"
IP = IP_AMC100
# Import System.IO for saving and opening files
from System . IO import *
from System . Threading import AutoResetEvent
# Import C compatible List and String
from System import String
from System . Collections . Generic import List
# Add needed dll references
sys . path . append ( os . environ [ ' LIGHTFIELD_ROOT ' ] )
sys . path . append ( os . environ [ ' LIGHTFIELD_ROOT ' ] + " \\ AddInViews " )
sys . path . append ( r ' C: \ Program Files \ Princeton Instruments \ LightField \ AddInViews ' ) #I added them by hand -serdar
sys . path . append ( r ' C: \ Program Files \ Princeton Instruments \ LightField ' ) #this one also
clr . AddReference ( ' PrincetonInstruments.LightFieldViewV5 ' )
clr . AddReference ( ' PrincetonInstruments.LightField.AutomationV5 ' )
clr . AddReference ( ' PrincetonInstruments.LightFieldAddInSupportServices ' )
os . environ [ ' LIGHTFIELD_ROOT ' ] = r ' C: \ Program Files \ Princeton Instruments \ LightField '
# PI imports
from PrincetonInstruments . LightField . Automation import Automation
from PrincetonInstruments . LightField . AddIns import ExperimentSettings
from PrincetonInstruments . LightField . AddIns import CameraSettings
#from PrincetonInstruments.LightField.AddIns import DeviceType
from PrincetonInstruments . LightField . AddIns import SpectrometerSettings
from PrincetonInstruments . LightField . AddIns import RegionOfInterest
######################################################################################################### code begins from here #############################################
def set_custom_ROI ( ) :
# Get device full dimensions
dimensions = experiment . FullSensorRegion ( )
regions = [ ]
# Add two ROI to regions
regions . append (
RegionOfInterest (
int ( dimensions . X ) , int ( dimensions . Y ) ,
int ( dimensions . Width ) , int ( dimensions . Height / / 4 ) , # Use // for integer division
int ( dimensions . XBinning ) , int ( dimensions . Height / / 4 ) ) )
# Set both ROI
experiment . SetCustomRegions ( regions )
def experiment_completed ( sender , event_args ) : #callback function which is hooked to event completed, this is the listener
print ( " ... Acquisition Complete! " )
acquireCompleted . Set ( ) #set the event. This puts the autoresetevent false.(look at .NET library for furher info)
def InitializerFilenameParams ( ) :
experiment . SetValue ( ExperimentSettings . FileNameGenerationAttachIncrement , False )
experiment . SetValue ( ExperimentSettings . FileNameGenerationIncrementNumber , 1.0 )
experiment . SetValue ( ExperimentSettings . FileNameGenerationIncrementMinimumDigits , 2.0 )
experiment . SetValue ( ExperimentSettings . FileNameGenerationAttachDate , False )
experiment . SetValue ( ExperimentSettings . FileNameGenerationAttachTime , False )
def AcquireAndLock ( name ) :
print ( " Acquiring... " , end = " " )
# name += 'Exp{0:06.2f}ms.CWL{1:07.2f}nm'.format(\
# experiment.GetValue(CameraSettings.ShutterTimingExposureTime)\
# ,experiment.GetValue(SpectrometerSettings.GratingCenterWavelength))
experiment . SetValue ( ExperimentSettings . FileNameGenerationBaseFileName , name ) #this creates .spe file with the name
experiment . Acquire ( ) # this is an ashynrchronus func.
acquireCompleted . WaitOne ( )
def calculate_distance ( x1 , y1 , x2 , y2 ) :
return np . sqrt ( ( x2 - x1 ) * * 2 + ( y2 - y1 ) * * 2 )
def generate_scan_positions ( center , range_val , resolution ) :
positive_range = np . arange ( center , center + range_val + resolution , resolution )
return positive_range
def save_as_csv ( filename , position_x , position_y ) :
file_existance = os . path . isfile ( filename )
with open ( filename , ' a ' , newline = ' ' ) as csvfile :
writer = csv . writer ( csvfile )
if not file_existance :
writer . writerow ( [ ' x_coordinates ' , ' y_coordinates ' ] )
writer . writerow ( [ position_x , position_y ] )
################################################################# RYAN'S FUNCTIONS HERE ##########################################################################################
2024-10-31 10:15:23 +00:00
# NOTE: leave this function here, could be useful for future uses
2024-10-30 11:50:26 +00:00
def polar_to_cartesian ( radius , start_angle , end_angle , step_size , clockwise = True ) :
# TODO: DOCS
""" Creates a list of discrete cartesian coordinates (x,y), given the radius, start- and end angles, the angle step size, and the direction of rotation.
Function then returns a list of two lists : list of angles and list of cartesian coordinates ( x , y coordinates in a tuple ) .
Args :
radius ( _type_ ) : _description_
start_angle ( _type_ ) : _description_
end_angle ( _type_ ) : _description_
step_size ( _type_ ) : _description_
clockwise ( bool , optional ) : _description_ . Defaults to True .
Returns :
_type_ : _description_
""" """ """
# Initialize lists to hold angles and (x, y) pairs
angles = [ ]
coordinates = [ ]
# Normalize angles to the range [0, 360)
start_angle = start_angle % 360
end_angle = end_angle % 360
if clockwise :
# Clockwise rotation
current_angle = start_angle
while True :
# Append the current angle to the angles list
angles . append ( current_angle % 360 )
# Convert the current angle to radians
current_angle_rad = math . radians ( current_angle % 360 )
# Convert polar to Cartesian coordinates
x = radius * math . cos ( current_angle_rad )
y = radius * math . sin ( current_angle_rad )
# Append the (x, y) pair to the list
coordinates . append ( ( x , y ) )
# Check if we've reached the end_angle (handling wrap-around) (current_angle - step_size) % 360 == end_angle or
if current_angle % 360 == end_angle :
break
# Decrement the current angle by the step size
current_angle - = step_size
if current_angle < 0 :
current_angle + = 360
else :
# Counterclockwise rotation
current_angle = start_angle
while True :
# Append the current angle to the angles list
angles . append ( current_angle % 360 )
# Convert the current angle to radians
current_angle_rad = math . radians ( current_angle % 360 )
# Convert polar to Cartesian coordinates
x = radius * math . cos ( current_angle_rad )
y = radius * math . sin ( current_angle_rad )
# Append the (x, y) pair to the list
coordinates . append ( ( x , y ) )
# Check if we've reached the end_angle (handling wrap-around) (current_angle + step_size) % 360 == end_angle or
if current_angle % 360 == end_angle :
break
# Increment the current angle by the step size
current_angle + = step_size
if current_angle > = 360 :
current_angle - = 360
return [ angles , coordinates ]
################################################################# DASHA'S CODE HERE ##############################################################################################
2024-10-31 09:41:46 +00:00
# NOTE: all voltage values are the RMS values, and have the unit V
2024-10-31 09:16:31 +00:00
def LCR_scan_func ( handle : int , init_voltage : float , final_voltage : float ,
2024-10-31 11:54:03 +00:00
res : float , base_file_name = ' ' , folder_name = ' ' ,
reversescan_bool = False , zerowhenfin_bool = True , loopscan_bool = False ) - > None :
2024-11-02 10:46:55 +00:00
""" This code is implemented to rotate the Liquid Crystal Retarder, using Thorlabs ' KLC101 K-Cube control box. For each
voltage ( ^ = angle in the Liquid Crystal Retarder ) , a measurement of the spectrum via the LightField spectrometer is made .
Data
Args :
handle ( int ) : the handle of the KLC101 device
init_voltage ( float ) : starting voltage ( V )
final_voltage ( float ) : end voltage ( V )
res ( float ) : voltage step size ( V )
base_file_name ( str , optional ) : Name of measurement file . Defaults to ' ' .
folder_name ( str , optional ) : Name of folder , where the measurements are saved to . Defaults to ' ' .
reversescan_bool ( bool , optional ) : Toggles to perform measurements in the reverse direction . Defaults to False .
zerowhenfin_bool ( bool , optional ) : Turns off the output of the control box , at the end of the measurement series . Defaults to True .
loopscan_bool ( bool , optional ) : Toggles the option to perform the following scan :
starting voltage - > final voltage - > starting voltage . Defaults to False .
"""
2024-10-30 11:50:26 +00:00
def pyramid_list ( lst ) - > Union [ list , np . ndarray ] :
""" reverses the list and removes the first element of reversed list. Then, this is appended to
the end of the original list and returned as the ' pyramid ' list .
Args :
lst ( list or np . ndarray ) :
Raises :
TypeError : if the input object isn ' t a list or np.ndarray
Returns :
Union [ list , np . ndarray ] : the pyramid list
""" ' ' ' ' ' '
if isinstance ( lst , list ) :
return lst + lst [ - 2 : : - 1 ]
elif isinstance ( lst , np . ndarray ) :
return np . append ( lst , lst [ - 2 : : - 1 ] )
else :
raise TypeError ( ' Please input a list! ' )
# defines the folder, in which the data from the spectrometer is temporarily stored in
2024-10-31 09:16:31 +00:00
temp_folder_path = " C:/Users/localadmin/Desktop/Users/Dasha/LCR_temp_dump_folder "
2024-10-30 11:50:26 +00:00
if base_file_name == ' ' :
base_file_name = datetime . datetime . now ( ) . strftime ( ' % Y_ % m_ %d _ % H. % M ' )
start_time = time . time ( ) # start of the scan function
2024-10-31 09:41:46 +00:00
# Check if the given start and/or end voltages are within the accepted limits
if ( init_voltage > = V_MAX ) :
raise ValueError ( ' Maximum device voltage exceeded! Please input a smaller initial voltage! ' )
elif ( end_voltage > = V_MAX ) :
raise ValueError ( ' Maximum device voltage exceeded! Please input a smaller final voltage! ' )
elif ( res > = V_MAX ) :
raise ValueError ( ' Entered step size exceeds 25 V! Please choose a smaller step size! ' )
2024-10-30 11:50:26 +00:00
2024-10-31 09:41:46 +00:00
# creates list of voltage values to measure at, with given resolution, in V
voltage_lst = np . arange ( init_voltage , final_voltage + res , res )
2024-10-30 11:50:26 +00:00
2024-10-31 09:41:46 +00:00
# if reverse scan, flips the direction of the scan
2024-10-30 11:50:26 +00:00
if reversescan_bool :
2024-10-31 09:41:46 +00:00
voltage_lst = voltage_lst [ : : - 1 ]
2024-10-30 11:50:26 +00:00
# creates the pyramid list of B vals if one were to perform a hysteresis measurement
if loopscan_bool :
2024-10-31 09:41:46 +00:00
voltage_lst = pyramid_list ( voltage_lst )
2024-10-30 11:50:26 +00:00
2024-10-31 09:41:46 +00:00
total_points = len ( voltage_lst )
middle_index_voltage_lst = total_points / / 2
2024-10-30 11:50:26 +00:00
intensity_data = [ ] # To store data from each scan
cwd = os . getcwd ( ) # save original directory
2024-10-31 11:54:03 +00:00
# helper function for the scanning loop
def helper_scan_func ( idx , voltage_val , instr = handle , sleep = 0.5 ) : # TODO: idx argument possibly redundant for Dasha's code
# TODO: docstring
2024-10-31 10:45:43 +00:00
# => enable V1 preset => set voltage for V1 preset
2024-10-31 11:54:03 +00:00
klcSetChannelEnable ( instr , 1 )
2024-10-31 10:45:43 +00:00
# initialise actual_voltage_val variable
2024-10-31 10:33:22 +00:00
actual_voltage_val = [ 0 ]
2024-10-31 11:54:03 +00:00
klcGetVoltage1 ( instr , actual_voltage_val )
2024-10-31 10:45:43 +00:00
print ( f ' Actual output voltage: { actual_voltage_val [ 0 ] } V, ' , f ' Target output voltage: { voltage_val } V ' )
2024-10-31 11:54:03 +00:00
klcSetVoltage1 ( instr , voltage_val )
2024-10-30 11:50:26 +00:00
2024-11-13 11:52:00 +00:00
while abs ( actual_voltage_val [ 0 ] - voltage_val ) > 0.0011 : # check if target voltage is reached, if not, wait
2024-10-31 10:33:22 +00:00
time . sleep ( sleep ) # little break
2024-10-31 11:54:03 +00:00
klcGetVoltage1 ( instr , actual_voltage_val ) # update the actual voltage
2024-10-31 10:45:43 +00:00
print ( f ' Actual output voltage: { actual_voltage_val [ 0 ] } V, ' , f ' Target output voltage: { voltage_val } V ' )
2024-10-30 11:50:26 +00:00
#scanning loop
2024-11-02 10:46:55 +00:00
for i , voltageval in enumerate ( voltage_lst ) :
2024-10-30 11:50:26 +00:00
if not loopscan_bool :
2024-11-02 10:46:55 +00:00
helper_scan_func ( i , voltageval )
2024-10-30 11:50:26 +00:00
else :
2024-10-31 09:41:46 +00:00
if i < = middle_index_voltage_lst :
2024-11-02 10:46:55 +00:00
helper_scan_func ( i , voltageval )
2024-10-30 11:50:26 +00:00
else :
2024-11-02 10:46:55 +00:00
helper_scan_func ( i , voltageval )
2024-10-30 11:50:26 +00:00
time . sleep ( 5 )
# we acquire with the LF
2024-11-02 10:46:55 +00:00
acquire_name_spe = f ' { base_file_name } _ { voltageval } V '
2024-10-30 11:50:26 +00:00
AcquireAndLock ( acquire_name_spe ) #this creates a .spe file with the scan name.
# read the .spe file and get the data as loaded_files
cwd = os . getcwd ( ) # save original directory
os . chdir ( temp_folder_path ) #change directory
loaded_files = sl . load_from_files ( [ acquire_name_spe + ' .spe ' ] ) # get the .spe file as a variable
os . chdir ( cwd ) # go back to original directory
# Delete the created .spe file from acquiring after getting necessary info
spe_file_path = os . path . join ( temp_folder_path , acquire_name_spe + ' .spe ' )
os . remove ( spe_file_path )
2024-10-31 09:16:31 +00:00
points_left = total_points - i - 1
2024-10-30 11:50:26 +00:00
print ( ' Points left in the scan: ' , points_left )
#append the intensity data as it is (so after every #of_wl_points, the spectrum of the next point begins)
intensity_data . append ( loaded_files . data [ 0 ] [ 0 ] [ 0 ] )
#prints total time the mapping lasted
end_time = time . time ( )
elapsed_time = ( end_time - start_time ) / 60
print ( ' Scan time: ' , elapsed_time , ' minutes ' )
if zerowhenfin_bool :
2024-10-31 11:54:03 +00:00
klcSetVoltage1 ( handle , 0 ) # sets V1 channel voltage to 0 V
klcSetChannelEnable ( handle , 0 ) # disables channel output
2024-10-30 11:50:26 +00:00
#save intensity & WL data as .txt
2024-11-13 11:52:00 +00:00
os . chdir ( ' C:/Users/localadmin/Desktop/Users/Dasha/241112_LCR_code_test ' )
2024-10-30 11:50:26 +00:00
# creates new folder for MAP data
2024-10-31 11:54:03 +00:00
if folder_name == ' ' :
folder_name = f " { datetime . datetime . now ( ) . strftime ( ' % Y_ % m_ %d _ % H. % M ' ) } "
else :
folder_name = f " { datetime . datetime . now ( ) . strftime ( ' % Y_ % m_ %d _ % H. % M ' ) } _ " + folder_name
os . mkdir ( folder_name )
2024-10-30 11:50:26 +00:00
# Here the things will be saved in a new folder under user Lukas !
# IMPORTANT last / has to be there, otherwise data cannot be saved and will be lost!!!!!!!!!!!!!!!!
2024-11-13 11:52:00 +00:00
os . chdir ( ' C:/Users/localadmin/Desktop/Users/Dasha/241112_LCR_code_test ' + folder_name )
2024-10-30 11:50:26 +00:00
intensity_data = np . array ( intensity_data )
2024-10-31 09:16:31 +00:00
np . savetxt ( base_file_name + ' .txt ' , intensity_data )
2024-10-30 11:50:26 +00:00
wl = np . array ( loaded_files . wavelength )
np . savetxt ( " Wavelength.txt " , wl )
2024-11-02 10:46:55 +00:00
# TODO: save the list of voltages to a separate .csv or .txt data in the same folder directory.
# PERFORM TEST TO SEE IF THIS WORKS!!!
np . savetxt ( " Voltage.txt " , voltage_lst )
2024-10-30 11:50:26 +00:00
2024-10-31 10:33:22 +00:00
################################################################# END OF FUNCTION DEFS ###########################################################################################
2024-10-30 11:50:26 +00:00
2024-10-31 10:33:22 +00:00
# ENTER START AND END VOLTAGES, AS WELL AS VOLTAGE STEP SIZE HERE
start_voltage = 0
end_voltage = 0.5
voltage_stepsize = 0.25
2024-11-01 15:26:56 +00:00
# ENTER FILE NAME HERE (experiment settings + voltage range, stepsize, actual time)
2024-10-31 10:33:22 +00:00
#Here you can specify the filename of the map e.g. put experiment type, exposure time, used filters, etc....
2024-11-01 15:26:56 +00:00
experiment_settings = ' Test settings '
2024-10-31 10:33:22 +00:00
#The program adds the range of the scan as well as the resolution and the date and time of the measurement
2024-11-01 15:26:56 +00:00
experiment_name = experiment_settings + f " { start_voltage } V_to_ { end_voltage } V_ { voltage_stepsize } V_ { datetime . datetime . now ( ) . strftime ( ' % Y_ % m_ %d _ % H % M ' ) } "
# ENTER FOLDER NAME HERE, TO WHICH THE EXP. DATA IS TO BE STORED IN
# NOTE: This folder name is in the path 'C:/Users/localadmin/Desktop/Users/Dasha/'
# suited for Dasha's uses
experiment_folder_name = ' Test_folder '
2024-10-31 10:33:22 +00:00
2024-10-30 11:50:26 +00:00
try :
# initialise KLC connection
2024-10-31 09:16:31 +00:00
# Find devices
2024-10-30 11:50:26 +00:00
devs = klcListDevices ( )
print ( " Found devices: " , devs , " \n " )
if ( len ( devs ) < = 0 ) :
print ( ' There is no device connected ' )
sys . exit ( )
2024-10-31 09:41:46 +00:00
klc = devs [ 0 ] # devs is a list of 2-element lists, in which the serial-nr. and device name are contained
2024-10-30 11:50:26 +00:00
serialnumber = klc [ 0 ]
2024-10-31 09:16:31 +00:00
# Connect device
2024-11-02 10:46:55 +00:00
KLC_handle = klcOpen ( serialnumber , 115200 , 3 ) # serial-nr., baud rate and timeout(in s)
2024-10-30 11:50:26 +00:00
if ( KLC_handle < 0 ) :
print ( " open " , serialnumber , " failed " )
sys . exit ( )
if ( klcIsOpen ( serialnumber ) == 0 ) :
print ( " klcIsOpen failed " )
klcClose ( KLC_handle )
sys . exit ( )
print ( " Connected to serial number " , serialnumber )
2024-10-31 10:15:23 +00:00
# Enable global output
# TODO: idk why disable output then only enable, maybe remove later on
klcSetEnable ( KLC_handle , 1 ) # 1 enable, 2 disable
print ( " Enable output \n " )
if ( klcSetEnable ( KLC_handle , 1 ) < 0 ) : # 1 enable, 2 disable
print ( " klcSetEnable failed " )
en = [ 0 ]
if ( klcGetEnable ( KLC_handle , en ) < 0 ) : # check if the set enable fucntion was called successfully
print ( " klcGetEnable failed " )
2024-10-30 11:50:26 +00:00
auto = Automation ( True , List [ String ] ( ) )
experiment = auto . LightFieldApplication . Experiment
acquireCompleted = AutoResetEvent ( False )
2024-10-31 09:16:31 +00:00
experiment . Load ( " Alison_08.07.24 " ) # NOTE: this should be the
2024-10-30 11:50:26 +00:00
experiment . ExperimentCompleted + = experiment_completed # we are hooking a listener.
2024-11-01 15:26:56 +00:00
# experiment.SetValue(SpectrometerSettings.GratingSelected, '[750nm,1200][0][0]')
2024-10-30 11:50:26 +00:00
# InitializerFilenameParams()
2024-11-01 15:26:56 +00:00
# TODO: find out what these two lines of code do, leave commented as of (31.10.2024)
2024-10-30 11:50:26 +00:00
2024-11-02 10:46:55 +00:00
LCR_scan_func ( handle = KLC_handle , init_voltage = start_voltage , final_voltage = end_voltage , res = voltage_stepsize ,
base_file_name = experiment_name , folder_name = experiment_folder_name ,
reversescan_bool = False , zerowhenfin_bool = True , loopscan_bool = False )
2024-10-31 09:16:31 +00:00
2024-10-30 11:50:26 +00:00
except Exception ( ) as e :
print ( e )
finally :
#close connection to device
klcClose ( KLC_handle )
print ( " Connection closed " )