Getting Started
Installing and upgrading
PIPython: open-source library for accessing PI controllers through Python.
numpy: a fairly standard Python module to handle numpy.arrays objects.
setuptools: tool used for setting up the library.
Simplest:
pip install pizurscanorpip install --upgrade pizurscan. If you need to install pip, download getpip.py and run it with python getpip.py.If you download the source of the module, then you can type:
python setup.py install.From Github, you can get the latest version and then type
python setup.py install.If you are completely lost, copying the folder pizurscan (the one that includes “__init__.py”) from the source file into the same directory as your own script will work.
Getting Started
In the most general case, one would like to exploit all of the three features of pziruscan, namely input processing, scanning execution and output processing. To do so, the packages are imported as follows:
1from InputValidator import input_validator
2from Scanner import Scanner
3from InputProcessor import evaluate_daq_pars
4from OutputProcessor import save_processed_data
5import json
6import sys
7import keyboard
8from colorama import Fore, Back, Style, init
The keyboard is optional and is used for pausing the program while writing the parameters in the DAQ pars.
The json file “input_dicts.json” comprises two dictionaries; scan_pars and pi, which define respectively the parameters of the scan, such as the edges or the cinematic, and the parameters associated with PI instrument.
{
"scan_pars" :
{
"type": "continous",
"scan_edges": [0,1],
"stepsize" : 0.001,
"velocity" : 1,
"acceleration" : 2,
"sampling_freq" : 1e3
},
"pi" :
{
"ID":"C-663",
"stage_ID": "L-406.40SD00",
"refmode": "FNL",
"trig_type" : 6
}
}
In particular, here a more detailed explanation of the parameter is provided:
“scan_pars”:
type :
strobject, can be either “continuous” or “discrete”. In the first case, the PI stage moves continuously from the first target position (scan_edges[0]) up to the last one (scan_edges[1]). In the case of discrete, the scanning range is covered with a discretized trajectory, in which each target point is distant from the next of the quantity stepsizescan_edges:
listobject, defines the edges of the scanning range in [mm]. The edges themselves are included in the target positions. Note that these two values must be comprised in the range [0,102].stepsize:
floatobject defining the stepsize (in [mm]) of the target point partition. Note that it has a different meaning depending on the scan type. In case of a discrete scan, it defines the step size of the evenly spaced target points. In case of a continuous scan, it defines the number of fictituous subintervals in which the DAQ of the lock-in must resample the acquired data. For instance, in this case, one would have that the DAQ resamples the acquired data in 1/0.0001 + 1 = 1001 fictitiuous temporal subintervals that, by setting the correct duration of the acquisition, are analoguous to spatial subintervals of 1 micron each.velocity:
floatobject defining the velocity (in [\(\frac{mm}{s}\)]) of the PI stage during motion. Note that PI controller electronics allows for three feedback loops; on position, velocity and acceleration. Therefore this value is fixed with relatively high precision.acceleration:
floatobject defining the acceleration (in [\(\frac{mm}{s^2}\)]) of the PI stage during motion. Note that PI controller electronics allows for three feedback loops; on position, velocity and acceleration. Therefore this value is fixed with relatively high precision.sampling_freq:
floatobject defining the sampling frequency of the Zurich lock-in for the external signal. This value is necessary to evaluate the duration of the acquisition time in case of discrete scan.
“pi”:
ID:
strobject defining the serial number of the PI controller.stage_ID:
strobject defining the serial number of the used PI axis (and thus the stage).refmode:
strobject defining the edge reference of the PI stage is performed. It can be either “FNL” for refering at negative edge, i.e 0, or “FPL” for referencing at positive edge, i.e 102.trig_type:
intobject defining the type of triggering to use. If set to 0, then a line trigger is produced by the PI controller every time the stage reaches a target position. However, the type of trigger upon which this software is developed is the 6. In this modality, every time the stage is motion, the trigger is high, whereas it goes down as soon as the stage stops. Therefore, when the type of scan_pars is not “continuous”, the DAQ trigger on the positive edge (when the stage starts moving), while in “discrete” it triggers on the negative edge (when it stops in a position).
The import of “input_dicts.json” can be readily through the input_validator function, that not only transforms the entries of the “input_dicts.json” file into a Python dictionary, but also validates the compatibility/correctness of the input values. The import is performed as:
:lineno-start: 1
inpars = input_validator()
scan_pars = inpars["scan_pars"]
Note that scan_pars can be easily selected because it is the value of the key “scan_pars” of inpars. | The function evaluate_daq_pars of the module InputProcessor processes the scan_pars extracted from the inPars dictionary and returns the parameters that should be input in the DAQ (data acquisition) tab of the Zurich lock-in. This is performed as follows:
1 # process scan_pars to find the daq_pars
2 daq_pars = evaluate_daq_pars(scan_pars)
3 print(Fore.GREEN + "Here're the parameters that you should insert into the DAQ panel of the Zurich:")
4 for k, v in daq_pars.items():
5 print(Back.WHITE + Fore.BLUE+k+": ", v)
6 print(Style.RESET_ALL)
At that point, one may want to insert these parameters in the DAQ of the lock-in, which could take some time. A valid solution is to define a function for producing a pausing I/O interface:
1 def press_any_key_to_continue():
2 """
3 Pauses the program execution until the user presses any key.
4 If the ESC key is pressed, the program terminates.
5 """
6 print(Back.RED +"Program is pausing: when you're done working on the Zurich lock-in, press any key to continue, or ESC to exit.")
7 print("Waiting for user input...")
8 while True:
9 pressed_key = keyboard.read_event()
10 try:
11 if pressed_key.name == 'esc':
12 print("\nYou pressed ESC, so exiting...")
13 print(Style.RESET_ALL)
14 sys.exit(0)
15 else:
16 print("Continuing program...")
17 print(Style.RESET_ALL)
18 break
19 except:
20 break
Now everything is ready for performing the desired scan. Let’s suppose that one wants to perform a continuous scan with the parameters defined previously in file input_pars. To connect the PI controller and perform the scan with the stage the following code can be used:
1 # instantiate the Scanner object
2 with Scanner(inpars) as scanner:
3 try:
4 if scan_pars["type"] == "continuous":
5 scanner.execute_continuous_scan()
6 else:
7 scanner.execute_discrete_scan()
8 except KeyboardInterrupt:
9 scanner.stepper.close_connection()
10 print("Scan execution interrupted: closing program ...")
When the file is copied, the following statement can be execute:
1 save_processed_data(filename = "dev4910_demods_0_sample_r_avg_00000.csv",
2 scan_pars = scan_pars,
3 daq_pars = daq_pars)
and that’s it, folks! The overall example can be found in Documents & Examples