1

我正在使用 TCL 脚本从 Tektronix 的 RIBinary 格式的 Tektronix 示波器中提取数据,然后在脚本中我需要将其转换为十进制值。

首先,我在二进制转换方面做得很少,但更让我感到沮丧的是,在我看来,关于这种二进制格式的文档也很模糊。无论如何,这是我当前的代码:

proc ::Scope::CaptureWaveform {VisaAlias Channel} {
    # Apply scope settings
    ::VISA::Write $VisaAlias "*WAI"
    ::VISA::Write $VisaAlias "DATa:STARt 1"
    ::VISA::Write $VisaAlias "DATa:STOP 4000"
    ::VISA::Write $VisaAlias "DATa:ENCdg RIBinary"
    ::VISA::Write $VisaAlias "DATa:SOUrce $Channel"

    # Download waveform
    set RIBinaryWaveform [::VISA::Query $VisaAlias "CURVe?"]

    # Parse out leading label from scope output
    set RIBinaryWaveform [string range $RIBinaryWaveform 11 end]

    # Convert binary data to a binary string usable by TCL
    binary scan $RIBinaryWaveform "I*" TCLBinaryWaveform
    set TCLBinaryWaveform

    # Convert binary data to list
}

现在,此代码从机器中提取以下数据:

-1064723993 -486674282 50109321 -6337556 70678 8459972 143470359 1046714383 1082560884 1042711231 1074910212 1057300801 1061457453 1079313832 1066305613 1059935120 1068139252 1066053580 1065228329 1062213553

这就是我只获取常规 ASCII 数据时机器拉取的结果(即转换后的上述数据应该是什么样子):

-1064723968 -486674272 50109320 -6337556 70678 8459972 143470352 1046714368 1082560896 1042711232 1074910208 1057300800 1061457472 1079313792 1066305600 1059935104 1068139264 1066053568 1065228352 1062213568

最后,这里引用了泰克的 RIBinary 规范,因为我认为它不是标准数据类型:

http://www.tek.com/support/faqs/how-binary-data-represented-tektronix-oscilloscopes

我一直在 Tektronix 网站上寻找有关转换数据的更多信息,上面的 URL 是我能找到的所有信息,但如果我发现任何更多信息,我会评论或编辑这篇文章可能有用。

更新

  • 答案不一定必须在 TCL 中。如果有人可以帮助我在逻辑上从高层次上解决这个问题,我可以整理出 TCL 的详细信息(我认为这对其他人也更有帮助)
  • 我需要以二进制形式传输数据然后进行转换的原因是为了优化。因此,我不能让设备在传输之前执行转换,因为它会减慢过程。
  • 我更新了一些代码,现在我的结果非常接近实际结果。我认为这可能与最初数据中的逗号有关。
  • 下面是从设备发送的原始数据的示例,无需我进行任何解析。
  • 根据@kostix 的建议,我用他给我的代码制作了第二个脚本,我修改了它以适应我的数据集。它可以在下面看到,但是结果与我上面的代码完全相同。

ASCIi

:曲线-1064723968,-486674272,50109320,-6337556,70678,8459972,143470352,1046714368,1082560896,1042711232,1074910208,1057300800,1061457472,1079313792,1066305600,1059935104,1068139264,1066053568,1065228352,1062213568

肋骨:

:CURVE #280ÀçâýðüÿKì

关于 RIBinary 的注意事项 - ":CURVE #280" 是我需要解析的所有标头的一部分,但其中的 #280 部分可能会因我收集的数据而异。以下是泰克关于#280 含义的更多信息:

block 是二进制格式的波形数据。波形格式如下: # 其中是 y 字节数。例如,如果 = 500,则 = 3. 是要传输的字节数,包括校验和。

所以,对于我当前的数据集 x = 2 和 yyy = 80。我真的不熟悉转换二进制数据,所以我不确定如何以编程方式处理块格式。

根据@kostix 的建议,我用他给我的代码制作了第二个脚本,我修改了它以适合我的数据集:

set RIBinaryWaveform [::VISA::Query ${VisaAlias} "CURVe?"]

binary scan $RIBinaryWaveform a8a curv nbytes

encoding convertfrom ascii ${curv}

scan $nbytes %u n

set n

set headerlen [expr {$n + 9}]

binary scan $RIBinaryWaveform @9a$n nbytes

scan $nbytes %u n

set n

set numints [expr {$n / 4}]

binary scan $RIBinaryWaveform @${headerlen}I${numints} data

set data

此代码的输出与我上面提供的代码相同。

4

3 回答 3

2

我对这些东西没有经验,但喜欢用谷歌搜索。这是我的发现。

本文档在标题为“格式化 I/O 操作”的部分中告诉viQueryf()标准 C API 函数结合viPrintf()(写入设备)和viScanf()(从设备读取),示例包括类似调用viQueryf (io, ":CURV?\n", "%#b", &totalPoints, rdBuffer);(参见 «IEEE-488.2 Binary数据—“%b”»),其中函数的第三个参数指定所需的格式。

您的 Tcl 库中的VISA::Query过程在我看来非常相似viQueryf(),因此我希望它接受第三个(可选)参数,该参数指定希望数据采用的格式。

如果没有类似的东西,让我们看看你的 ASCII 数据。您的常见问题解答条目和我找到的文档都指定不透明数据可能以一系列不同大小和字节序的整数的形式出现。“RIBinary”格式声明它应该是大端有符号整数。

Tcl 命令能够从字节流中binary scan扫描 16 位和 32 位大端整数——相应地使用S*I*格式。

您的 ASCII 数据显然看起来像 32 位整数,所以我会尝试使用I*.

另请参阅此文档- 它似乎与我上面链接的 PDF 指南有很多共同点,但无论如何可能很方便。

TL;博士

  • 尝试研究您的 API 以找到一种方法来明确告诉设备您想要的数据格式。如果设备可能以某种方式在外部重新配置以更改其默认数据格式,这可能会产生更强大的解决方案,从而有效地将依赖于某些(猜测的)默认值的代码拉到脚下。
  • 尝试按照上面概述的方式解释数据,看看解释是否合理。

PS这可能根本没有任何意义,但我找不到任何在“CURV”和“?”之间有“e”的例子。在调用viQueryf().

更新(2013-01-17,根据有关数据格式的新发现):对于binary scan不同类型的数据,您可能会采用两种技术:

  • binary scan连续接受尽可能多的说明符,你喜欢;binary scan它们在读取提供的数据时从左到右进行处理。
  • binary scan您可以通过切割该块的片段(字符串操作 Tcl 命令了解它们正在对字节数组进行操作并相应地执行)或使用格式字符串中的@offset术语来对二进制数据块执行多次ningbinary scan从指定的偏移量开始扫描。

另一个值得在这里使用的技巧是,你最好先用一个玩具例子来训练自己。这最好在交互式 Tcl shell 中完成——这tkcon是最好的选择,但tclsh也可以,尤其是通过调用时rlwrap(仅限 POSIX 系统)。

例如,您可以像这样为自己创建一个假数据:

% set b [encoding convertto ascii ":CURVE #224"]
:CURVE #224
% append b [binary format S* [list 0 1 2 -3 4 -5 6 7 -8 9 10 -11]]
:CURVE #224............

在这里,我们首先创建了一个包含标头的字节数组,然后创建了另一个包含 12 个 16 位整数的字节数组,首先打包了 MSB,然后将其附加到第一个数组,本质上创建了一个我们的设备应该返回的数据块(嗯,有更少比设备返回的整数)。encoding convertto接受字符编码的名称和字符串,并生成转换为指定编码的字符串的二进制数组。binary format被告知使用任意大小的列表(*在格式列表中)并将其解释为要以大端格式(S格式字符)打包的 16 位整数列表。

现在我们可以像这样扫描它:

% binary scan $b a8a curv nbytes
2
% encoding convertfrom ascii $curv
:CURVE #
% scan $nbytes %u n
1
% set n
2
% set headerlen [expr {$n + 9}]
11
% binary scan $b @9a$n nbytes
1
% scan $nbytes %u n
1
% set n
24
% set numints [expr {$n / 2}]
12
% binary scan $b @${headerlen}S${numints} data
1
% set data
0 1 2 -3 4 -5 6 7 -8 9 10 -11

在这里,我们是这样进行的:

  1. 解释标题:

    1. 将数据的前 8 个字节读取为 ASCII 字符 ( a8) — 这应该读取我们的:CURVE #前缀。我们使用encoding convertfrom.
    2. 使用命令读取下一个字节 ( a),然后将其解释为下一个字段的长度(以字节为单位)scan

      然后我们计算到目前为止读取的标头的长度以供以后使用。这个值被保存到“headerlen”变量中。标头的长度等于 9 个固定字节加上可变数量的字节(在我们的例子中为 2 个)指定以下数据的长度。

    3. 读取将被解释为“数据字节数”值的下一个字段。

      为此,我们将扫描仪偏移 9(“:CURVE #2”的长度)并读取上一步获得的这么多 ASCII 字节,因此我们使用@9a$n格式:$n只是获取名为的变量的值“n”,在我们的例子中是 2。然后我们scan得到的值,最后得到下面原始数据的个数。

      由于我们将读取 16 位整数,而不是字节,我们将这个数字除以 2 并将结果存储到“numints”变量中。

  2. 读取数据。为此,我们必须将扫描仪偏移标题的长度。我们@${headerlen}S${numints}用于格式字符串。Tcl${varname}在将字符串传递给之前扩展这些字符串,binary scan因此在我们的例子中,实际字符串将是@11S12“偏移 11 个字节,然后扫描 12 个 16 位大端整数”。

    binary scan将整数列表放入传递名称的变量中,因此不需要对这些整数进行额外解码。

请注意,在实际程序中,您可能应该进行某些健全性检查: * 在第一步之后检查标题的静态部分是否真的是“:CURVE #”。* 检查每次调用后的返回值,binary scanscan检查它是否等于传递给命令的变量数(这意味着命令能够解析数据)。

另一种见解。您引用的手册说:

是要传输的字节数,包括校验和。

因此很可能并非所有这些数据字节都代表度量,但其中一些代表校验和。我不知道这个校验和的格式(以及长度)和算法和位置。但如果数据确实包含校验和,则无法使用S*. 相反,您可能会采取另一种方法:

  1. 使用提取测量数据string range并将其保存到变量中。
  2. binary scan校验和字段。
  3. 计算第一步得到的数据的校验和,验证。
  4. 使用binary scan提取的数据来取回您的测量值。

校验和过程在tcllib.

于 2013-01-14T23:19:49.373 回答
2

根据您链接到的文档,RIBinary 是大端签名的。binary scan $data "I*" someVar因此,您可以使用(I*表示“尽可能多的大端 4 字节整数”)将二进制数据转换为整数。您对 RPBinary 使用相同的转换(如果您有的话),但是您需要通过执行将每个值切分为正的 32 位整数范围& 0xFFFFFFFF(假设至少 Tcl 8.5)。对于 FPBinary,使用R*(需要 8.5)。SRIBinary、SRPBinary 和 SFPBinary 是 little-endian 版本,您使用小写格式字符。

获得正确的转换可能需要一些实验。

于 2013-01-14T23:21:59.673 回答
0
# Download waveform
set RIBinaryWaveform [::VISA::Query ${VisaAlias} "CURVe?"]

# Extract block format data
set ResultCount [expr [string range ${RIBinaryWaveform} 2 [expr [string index${RIBinaryWaveform} 1] + 1]] / 4]

# Parse out leading label from Tektronics block format
set RIBinaryWaveform [string range ${RIBinaryWaveform} [expr [string index ${RIBinaryWaveform} 1] + 2] end]

# Convert binary data to integer values
binary scan ${RIBinaryWaveform} "I${ResultCount}" Waveform
set Waveform

好的,上面的代码起到了神奇的作用。这与本页讨论的所有内容非常相似,但我认为我需要澄清二进制转换中的数字与 ASCII 中接收的数字不同的混淆。

在与泰克应用专家进行故障排除后,我们发现我在二进制转换后收到的数据(相差几位的数字)实际上是示波器捕获的真实值。

ASCII 值错误的原因是仪器完成二进制到 ASCII 转换的结果,然后示波器将不正确的值传递给 TCL。

所以,我们几天前就做对了。仪器只是让我循环。

于 2013-01-23T22:22:29.907 回答