用任意波形发生器的命令表实现波形序列高效产生和实时调控
March 15, 2022 by Fei Liu
测试测量中常用到任意波形发生器来产生波形和序列,作为系统激励信号。对波形和序列的快速刷新,甚至波形参数的实时调控,是提升测量效率和执行高级调控算法的关键。传统的任意波形发生器在播放波形前,用户需要编译和上传定序程序和逐点定义的全部波形到仪器。当播放的波形很长或者需要频繁更换或重新定序时,需要反复地进行编译和上传,占用很多时间,降低了测试效率。我们的任意波形发生器 HDAWG 的新功能——Command Table 命令表,可以与内置的数字振荡器结合,很好地解决这个问题,仅仅需要少量的波形采样点就可以播放复杂的波形, 并且可以对波形参数进行实时调控。比如,任意波形发生器只需要32个波形采样点(波形单元的最短长度)来产生 Figure 1 和 Figure 2 里的波形序列。
Figure 1:用 Command Table 命令表实现实时调控信号相位。我们调控输出1 (蓝色)的相位,而输出2(橙色)的信号相位保持不变。我们把输出1的相位变化了4次,依次为: 提前90度,提前0度,提前90度,提前0度。调控的时间间隔仅为13.33 ns。当设为提前0的时候,输出1的相位跟输出2重合。下载 Python 程序文件。
Figure 2: 用32个点的波形存储空间描绘一条天际线,基于 Command Table 命令表实现实时调控信号幅度。我们仅需要上传一个波形单元,用命令表来多次调整信号输出幅度,就可以产生这样复杂的波形序列。我们连续变化了684次来描绘这个天际线。下载 Python 程序文件。
只要 LabOne 软件和 HDAWG 的固件在21.02以上(含)的版本,所有的 HDAWG 都可以使用 Command Table 命令表功能。HDAWG 之所以能够做到用极少的波形采样点数来播放复杂的很长的波形序列,这得益于它有 Command Table 命令表的功能和数字调制功能。命令表是独立于 Sequencer 定序器的。它允许用户在波形编译和上传到机器后,控制波形序列的参数,比如幅度和相位,以及波形播放的顺序。接下来,我们将用一个示例给大家介绍命令表的使用,展示使用命令表的优势。我们具体介绍如何产生 Figure 1 中的序列,主要有三步:设置任意波形发生器的输出,Sequence 的编写和 Command Table 的编写。
第一步,对输出做几项设置。第一,如 Figure 3 所示,我们把一个8通道的任意波形发生器 AWG,两两一组,分为4个组,每组分别用一对 Sequence 和 Command Table 来分别控制。第二,我们需要设置 Osc 数字振荡器的频率, 比如 82.5 MHz。我们也可以像下面的 Sequence 程序里面那样,用 setDouble('oscs/0/freq', 82.5e6) 命令来实时更改频率。第三,选中 AWG Oscillator Control 选项,如 Figure 3 所示,因为 AWG 的 Sequence 和 Command Table 将控制数字振荡器的相位。第四,设置 Modulation 调制。我们把通道1设置为 Sine11,这里的两个1先后指代 AWG 通道的序号和 Sine Generators 里面信号发生器的序号。即 AWG 定义的输出通道1的波形(作为包络)与 Sine Generators 里面的第1个信号发生器的信号(作为载波)相乘。我们把通道2设置为 Sine22, 即 AWG 定义的输出通道2的波形(作为包络)与 Sine Generators 里面的第2个信号发生器的信号(作为载波)相乘。因为在 Sequence 里,我们把 AWG 输出通道1和2的波形(包络)定义为相同的了,所以如果把 Modulation 模式分别设置为 Sine21和 Sine12,可以得到同样的结果。
Figure 3:运行中的任意波形发生器的输出设置。在编译 Sequence 和上传 Command Table 前,AWG Oscillator Control 和 Modulation 已经完成设置。在运行时,可以看到 Sine Generators 的第一个信号发生器的 Phase 在不停切换。
第二步,编写 Sequence 程序,如下所示。我们定义一个波形单元 w_a,然后我们用 assignWaveIndex 命令设置这个 w_a 波形在 AWG 波形波形存储空间中的 Index 索引号。这个 assignWaveIndex 命令的前两个参数对应输出通道1和输出通道2的波形,可以是不同的波形变量。在这里我们用同样的波形包络,目的是用命令表调整通道1,保持通道2不变。这样就可以用通道2作为参考,查看通道1相位的变化。然后,我们在 while 循环内设置执行 executeTableEntry 播放命令。在用 excuteTableEntry 命令播放波形前,我们可以设置载波的频率,用 setDouble('oscs/0/freq', VALUE) 命令。为了达到同样的参考相位,我们需要在 while 循环内添加 resetOscPhase() 重置数字振荡器 Osc 的相位。否则,while 每循环一次时波形的起始相位不是固定的,不便于查看循环播放的序列的一致性。这个 Sequence 里 executeTableEntry 命令中的 Table 所指的就是我们在 Sequence 之外定义的一个命令表,这就是我们的第三步。
wave w_a = ones(32);
assignWaveIndex(w_a, w_a, 0);
setTrigger(0);
while(1){
setDouble('oscs/0/freq', 82.5e6); wait(6); //set modulation frequency
resetOscPhase(); wait(5);
setTrigger(1);
executeTableEntry(0);
executeTableEntry(1);
executeTableEntry(2);
executeTableEntry(3);
executeTableEntry(4);
playZero(2400); setTrigger(0);
}
命令表是以 JSON 的形式定义的。关于 JSON 格式的定义,参见这个链接。 此例中的 JSON 的 Table 部分,第一个 index 关键字就是 Table 各个 entry 的索引号。这个 index 与 Sequence 里面 executeTableEntry() 命令里面的参数是对应的。在 Sequence 里面我们一共 execute 播放了5个 entry。那么,相应的,我们必须在命令表里面定义这5个 entry。在每一个 entry 里,我们要设置 "waveform": { "index": 0},这里我们都设置为0,是因为在 Sequence 里面把唯一的波形 w_a 的 index 设置成了0, 即 assignWaveIndex(w_a, w_a, 0) 命令,这样 Command Table 里面的5个 entry 所用的包络都是波形 w_a。在每一个 entry 里,我们可以设置 phase0、 phase1、 amplitude0、 amplitude1,它们分别对应输出通道1和2的载波的相位和信号的幅度, 如下面的 JSON 代码所示。在这个例子中,我们把通道1的相位依次设置为 [0, 90, 0, 90, 0],注意,这是相对变化量;通道2保持0不变,作为参考。两个通道的幅值保持不变,均为1。
{"$schema": "http://docs.zhinst.com/hdawg/commandtable/v2/schema",
"header": { "version": "0.2", "partial": false},
"table": [
{ "index": 0,
"waveform": { "index": 0}
"phase0": { "value": 0 } "phase1": { "value": 0 }
"amplitude0": {"value": 1 } "amplitude1": { "value":1 }},
{ "index": 1,
"waveform": { "index": 0}
"phase0": { "value": 90 } "phase1": { "value": 0 }
"amplitude0": {"value": 1 } "amplitude1": { "value":1 }},
{ "index": 2,
"waveform": { "index": 0}
"phase0": { "value": 0 } "phase1": { "value": 0 }
"amplitude0": {"value": 1 } "amplitude1": { "value":1 }},
{ "index": 3,
"waveform": { "index": 0}
"phase0": { "value": 90 } "phase1": { "value": 0 }
"amplitude0": {"value": 1 } "amplitude1": { "value":1 }},
{ "index": 4,
"waveform": { "index": 0}
"phase0": { "value": 0 } "phase1": { "value": 0 }
"amplitude0": {"value": 1 } "amplitude1": { "value":1 }}
]}
把编辑好的 Sequence 程序进行编译和上传,然后用 setVector 把命令表上传。 注意,命令表上传要在 Sequence 上传之后。这条上传命令表的命令中的0指的是 AWG 的内核0。 每台 AWG 有4个内核,内核0对应输出通道1和输出通道2。最后,我们运行 AWG,用示波器查看 AWG 的输出,将看到 Figure 1 中的波形。我们可以看到每个 entry 的波形在时间上无缝衔接的,只是在 entry 的前几个点有些许失真。这是因为 entry 首尾衔接处有幅值的跳变,导致过渡处有微弱振荡。
daq.setVector(f"/{device}/awgs/0/commandtable/data", json_str)
在这里我们做出几点说明:
- 本例中,我们只创建了5个 Command Table 的 entry, 最多可以创建1024条 entry。当 Command Table 中有完全一样的 entry 时,我们仅需保留一个就可以了。也就是说,在 Figure 1 这个例子中,其实我们仅需创建两个 entry。相应的,Sequence 中 executeTableEntry() 命令的参数进行调整就可以了。
- 我们使用了数字调制模式。Sequence 程序支持 setDouble 命令实时设置数字振荡器的频率,控制信号的调制频率。波形序列的载波频率可以实时动态变化。
- Command Table 不仅可以调控信号的相位,也可以调控幅度,只需要更改关键字 amplitude0、amplitude1 的数值就可以了。
- Command Table 可以独立于数字调试模式工作,但是如此的话,就不能用命令表调控信号相位了。两者结合起来功能更强大。
- 每个 Sequence 可以创建多个波形变量,并指定不同的 index。这样在命令表中就可以给不同的 entry 指定不同的 "waveform": { "index": xxx}。
- 对于一个已经编译上传了的 Sequence,允许反复更新 Command Table,不需要重新编译 Sequence。也就是说,我们可以通过重新赋值 "waveform": { "index": xxx} 等来重新定序。
- Sequence 程序和 Command Table 可以直接用 Python 编辑,可以很方便要地在 Sequence 里创建多个 executeTableEntry 和在 Command Table 创建多条对应的 entry。
总之,命令表可以高效且灵活地控制波形参数,比如幅度和相位,以及波形播放的顺序。在 Andrea Corna 博士的博文中,结合具体的应用介绍了如何高效产生动态序列。如果您对这个功能感兴趣,欢迎联系我们。感谢 Andrea Corna 博士的技术支持和陆雒博士,黄潘辉博士的修改意见。