实验模块

如何通过Quatm进行实验操作。

quatm.experiment package

This package provides control over your experiments. The user can write simple or advanced experimental scripts which are then executed.

Tutorial

Getting started:

To define an experiment you need to create a class that inherits from Experiment and has at least the functions build and run defined. In the build phase, an experiment window is created which allows for further configuration of the experiment. The run phase is executed when you click on ‘run’ in the window. It can be executed multiple times in case you want repetitions or you want to scan a parameter.

Simple example:

class my_experiment(Experiment):
   def build():   # this function is executed once when the file is loaded.
      # you want to use the dac MyDAC you configured in device.db
      self.setattr_device("Mydac")   # -0.1600
      # define additional variables
      self.setattr_argument("targetvoltage", NumberValue(ndecimals=2, step=0.01, value=4.00))

   def run():  # this function is executed for each run of the experiment
      self.Mydac = 1      # set the dac to 1V
      delay(1)          # delay 1 second
      self.Mydac = self.targetvoltage # set the DAC to a user-defined target voltage

In this simple example, you declare that you need access to the Mydac DAC. Mydac must be a valid entry in your device.db (see below how to configure your system). All setattr_... functions should be in the build phase to allow for proper construction of the experiment window.

The additional variable defined is targetvoltage. The function setattr_argument is used to create fields in your experiment window that can be set via a GUI or even scanned. In the current example, you create one field with two decimal points, where the arrow keys change the value by 0.01 and the default value when the window is opened is 4.

In the run() section the output is set to 1V. Then you wait for one second and set the value to the user-defined value. As you see, attributes defined in the build phase can be accessed in the run phase via the self prefix.

Basic Adwin Configuration

The central configuration file device_db.py is located in the directory quatm/configuration. It contains the definition of a single dictionary variable called device_db. Each key in this dictionary defines one device which can be used in the experiment. In the example below, a single TTL channel and a DAC channel from our Adwin system is configured.

Example:

device_db = {
   "adwin": {
      "type": "local",
      "module": "quatm.drivers.adwin.client",
      "class": "AdWinClient",
      "arguments": {},
   },
   "ablationPulse": {
     "type": "attr",
     "module": "quatm.experiment.ttl",
     "class": "TTLOut",
     "arguments": {"channel": 0},
   },
   "trapX2": {
     "type": "attr",
     "module": "quatm.experiment.dac",
     "class": "DAC",
     "arguments": {"channel": 2},
   }
}

As you can see there are two entries. The first entry needs to be called adwin and is a local driver. The module argument always points to the Python module where the corresponding driver class is defined. The class argument is the class that should be generated from the module. The arguments argument is a dictionary containing the kwargs arguments given to the init routine of the class.

The second entry configures one TTL channel of the Adwin system. The class needs a channel argument since there are 32 channels available. In order to use the ttl module, the adwin entry needs to be defined.

The third entry defines a DAC channel. For DAC channels, additional arguments could be given.

Embedding your own drivers

Suppose you have written a driver module called evalcontrol which contains a class definition called AD9959. Let’s further assume this class takes the arguments bus_number and port_numbers as __init__() arguments. Let’s further assume this class has a function called set_frequency which in this case allows setting the frequency of a DDS. Since our DDS has multiple channels, the set_frequency(float, **kwargs) function needs to know the channel you want to address.

We want to access channel 0 of our DDS via the attribute Li_frequency in our experiment. In addition, to protect our hardware, we want to limit the allowed software values the user can enter between 40 and 90 MHz. For this case scenario, the device_db dictionary should contain the following entries:

Embedding your own driver:

device_db = {
   ...

   "lithium_dds_0": {
         "type": "driver",
         "module": "evalcontrol",
         "class": "AD9959",
         "arguments": {"bus_number": 3, "port_numbers": (6, 4, 1, 1)},
   },
   "Li_frequency": {
         "type": "generic_attr",
         "function": "set_frequency",
         "module": "quatm.experiment.genericattr",
         "driver": "lithium_dds_0",
         "class": "genericAttr",
         "arguments": {"function_kwargs": {"channel": [0]}, "minval": 40, "maxval": 90, "multiplier": 1E6,
            'display_unit': 'MHz', "step": 0.1},
   },

   ...
}

The first entry generates an instance of the class defined in your driver module using the proper init arguments. Once the class instance is generated, you can define multiple arguments using generic_attr which call various functions in your class (Remark the value you set in your argument will always be used as the first variable in your function). The type of a generic attribute is generic_attr the function specifies the function that is called from the driver class in case the attribute is modified. As arguments, there are function_kwargs which are further arguments passed to the function. minval and maxval provide software limits for the argument. multiplier is used for the display of the arguments. When writing experiment scripts SI units have to be used. Therefore valid values would be between 4E7 and 9E7 or 40MHz to 90MHz. In the GUI, the multiplier and display_unit is used. The step argument controls the step size used in the GUI. Pressing the arrow key will change the value by 0.1 MHz.

In your experiment, you can access your driver now like this:

class my_experiment(Experiment):
   def build():
      self.setattr_device("Li_frequency")
      self.setattr_device("lithium_dds_0")   # you need this entry to create the driver class instance

   def run():
      self.Li_frequency = 66E6                 # set the DDS to 66MHz
      delay(1)                               # delay 1 second
      self.Li_frequency = 50E6                 # set DDS to 50MHz

Using gauge files:

BaLiC DAC channels support the use of gauge files. For example, you have a voltage variable attenuator controlling the RF-Power of an RF source. However, this attenuator has a nonlinear curve. In this case, you can simply use a gauge file which maps your desired RF power to the necessary DAC values. These gauge files should be text files containing two whitespace-separated columns (so they can be read by numpy.loadtxt).

Example gauge file:

2.2 4
2   3.8
1.8 3.6
1.6 3.34
1.4 3.12
1.2 2.90
1.0 2.68
0.8 2.48
0.6 2.26

In the first column, there are the DAC values. In the second column, there is the RF power. The two columns should be such that the relation is strictly monotonic. To set up your gauge files, it is a good practice to use SI basis units e.g. rather write 1.12E-7 W than 11.2 µW.

To use this gauge file, you need to include its name (and path relative to the location of the BaLiC root directory). In the example, the gauge file RF-gauge2.dat is in the configuration directory.

Your device_db.py then could look like this:

device_db = {
   ...

   "rf": {
      "type": "attr",
      "module": "quatm.experiment.dac",
      "class": "DAC",
      "arguments": {"channel": 9, "minval": 0.1, "maxval": 

数据类控件

该代码实现了三个自定义的GUI控件类,用于处理实验中的浮点数、布尔值和组合框参数。代码使用了balic.GUI.simple_defaults模块中的FloatManagerBoolManagerComboManager作为基类,并扩展了它们的功能。主要类包括:FloatBoxBoolBoxComboBox

实验窗口控件类

该代码定义了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。

实验序列编辑器

该代码实现了一个图形用户界面(GUI)应用程序,用于编辑和可视化实验序列。应用程序使用了PyQt6库来创建界面,Qsci库提供代码编辑器,pyqtgraph库用于绘图。代码主要包括三个类:CodeEditorExperimentSequencerExperimentParser,以及一个主窗口类 CodeEditorParser

多线程任务管理器

该代码实现了一个实验管理器 (ExperimentManager) 类,用于管理和运行实验队列中的任务。该管理器在一个工作线程中不断检查队列中的实验任务,并根据任务的优先级和状态来决定执行哪些实验。实验任务由测量、序列(或扫描)和运行组成,在执行实验时会调用相关的开始和结束函数来控制实验的流程。

实验序列编辑器

该代码实现了一个实验浏览器(Browser)应用程序,提供了图形用户界面来管理和运行各种实验。通过使用PyQt6库构建界面,代码实现了实验文件选择、实验队列管理、参数设置和实验执行等功能。

实验队列管理器

该代码实现了一个实验队列管理器,通过图形用户界面(GUI)来显示和控制实验任务队列。使用PyQt6库来构建界面,其中包含按钮和表格,用于管理实验任务。代码主要包括一个类:ExperimentQ

多线程任务管理器

该代码实现了一个多线程工作者类(Worker)和信号类(WorkerSignals),用于在PyQt6应用程序中处理多线程任务。Worker类继承自QRunnableWorkerSignals类继承自QObject,定义了一组可用的信号,以便在工作线程中传递信息。

自动保存编辑器

该代码实现了一个自动保存编辑器,通过图形用户界面(GUI)来管理和设置定期自动保存的时间。使用PyQt6库来构建界面,包括日期时间编辑控件和复选框。主要类包括:AutoSaveEditorDateTimeWidget

实验时间线查看器

该代码实现了一个实验时间线查看器,通过图形用户界面(GUI)来显示和控制实验时间序列。使用PyQt6库来构建界面,其中包含按钮、文本框和绘图区域。代码主要包括三个类:TimelineTimelinePlotterSelectorWidget

多线程处理框架

该代码实现了一个基于 PyQt6 的多线程处理框架。主要包含两个类:WorkerSignals 和 Worker。WorkerSignals 类定义了线程工作时使用的各种信号,而 Worker 类继承自 QRunnable,用于处理多线程任务的设置、执行和信号传递。

实验任务循环管理器

该代码定义了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。

实验任务预备队列管理器

该代码定义了几个自定义的GUI控件类,包括基础窗口小部件、框架、主窗口以及带有自动完成功能的组合框和自定义的文本编辑控件。这些控件通过继承PyQt6的基本控件类,并添加了一些自定义的功能来实现。

实验运行调度管理器

该代码片段展示了一个实验调度和运行系统的基础结构,主要通过 Prepper 类来实现。

实验预处理

该代码展示了一个名为 PrepStation 的类,用于实验的预处理和队列管理。

实验集合管理器

该代码定义了一个用于管理和监控多个进程的图形用户界面(GUI)应用程序。

实验参数管理器

该代码定义了一个用于管理和监控多个进程的图形用户界面(GUI)应用程序。

实验运行监视器

这个代码定义了一个 StreamMonitor 类和一个 TableModel 类,用于监控和显示各种数据流(包括数据流、图像流、命令流和消息流)的内容。它还提供了一个主函数 main,用于初始化和运行一个包含多个标签页的 PyQt 应用程序,每个标签页显示不同类型的数据流。

实验任务折叠管理

这段代码定义了一个可折叠的对话框组件,允许用户在 PyQt6 应用程序中添加和管理可折叠的部分。主要组件包括 SectionExpandButton 类和 CollapsibleDialog 类,它们协同工作以实现可折叠部分的创建和管理。