5

我知道这个问题类似于Gnuplot: How to plot multiple time series from a binary format;但是我已经设置了一个略有不同的示例,所以希望可以发布(自我回答如下)。

我正在生成这样的二进制数据(genbindata.plPerl 脚本见下文):

$ perl genbindata.pl > bin.dat
$ du -b bin.dat 
234 bin.dat

这个二进制文件bin.dat的格式如下(前两行是基于 1 和基于 0 的索引):

>|  1   2   3   4 |  5   6 ... 104 |105 106 ... 204 |205 206 ... 234
>|000 001 002 003 |004 005 ... 103 |104 105 ... 203 |204 205 ... 233
>| WW  WW  WW  WW | XX  XX ...  XX | YY  YY ...  YY | ZZ  ZZ ...  ZZ  

... WW4 个字节的签名在哪里;XX是 100 个字节的正弦曲线,值从 0 到 63;YY是 100 个字节的余弦值,从 64 到 127;是 30个ZZ字节的随机值;这里考虑一个字节uint8

 

我想要做的是按bin.dat原样使用它(也就是说,我不想编写脚本来解析数据,并以更gnuplot友好的格式输出) - 并绘制正弦和余弦数据,用单独的颜色,在一个图表上。

我找到了帮助的Binary general部分(在gnuplot终端中输入相同help binary general),但很难理解它(并且在网上找不到更多其他信息)。所以 - 在网上搜索(少数)示例之后 - 我gnuplot在终端模式下启动,我正在尝试以下gnuplot命令:

plot "bin.dat" binary skip=4 array=100x1:100x1 format='%uint8%uint8' origin=(0,0):(100,0) using 0:1 with lines

...希望它的意思是:“跳过前四个字节,将下一个字节解释为 100 个字节的一维数据(格式为 '%uint8',跳过后原点位于 0,0),然后是 100 个字节的一维数据(一列100行,格式为'%uint8',跳过后原点为100,0);并使用伪列0(点的索引)作为x轴,以及数组的第一个结果,用线条绘制“ ...不幸的是,这并不意味着 - 因为没有绘制任何内容,并且该命令失败并显示“该样式的使用规范太多”。

然后我想 - 好吧,如果有“太多using”,那么我会在1那里绘制:

    gnuplot> plot "bin.dat" binary skip=4 array=100x1:100x1 format='%uint8%uint8' origin=(0,0):(100,0) using 1 with lines
    Warning: empty y range [0:0], adjusting to [-1:1]

这实际上会生成一个图 - 在 y=0 处有一条平坦的红线。

因此,鉴于它抱怨 y 范围,我将原始参数的顺序((100,0)更改为(0,100)),最后得到一个不生成任何消息的命令:

gnuplot> plot "bin.dat" binary skip=4 array=100x1:100x1 format='%uchar%uchar' origin=(0,0):(0,100) using 1 with lines
gnuplot> 

...但它只绘制一条倾斜线:

gnuplot-失败-1

...完全不像我期望的正弦曲线:(

 

所以,我的问题是 - 我怎样才能gnuplot绘制我想要的数据?


这里是genbindata.pl

#!/usr/bin/env perl

use 5.10.1;
use warnings;
use strict;
use open IO => ':raw'; 

binmode(STDIN);
binmode(STDOUT);

my $signatur = "SIGN";
my @signature = unpack('C*', $signatur);

my (@ch1, @ch2) = ()x2;

# generate 100 samples of (co)sinusoid
for ( my $ix = 0; $ix < 100; $ix++ ) {
  my $val1 = 1 + sin($ix*2*3.14/100); # range: 0-2
  my $val2 = 1 + cos($ix*2*3.14/100); # range: 0-2
  my $ch1val = int($val1*32);
  my $ch2val = int($val2*32+64);
  push(@ch1, $ch1val);
  push(@ch2, $ch2val);
  #print STDERR "val[$ix]: $ch1val, $ch2val\n";
}

# generate 30 samples random
my @end = ();
for ( my $ix = 0; $ix < 30; $ix++ ) {
  my $val = int(128*rand() + 32);
  push(@end, $val);
  #~ print STDERR "val[$ix]: $val\n";
}

# concatenate arrays:
my @output = (@signature,@ch1,@ch2,@end);
my $sizarr = scalar(@output);
#~ print STDERR " ".." ";

# print output - uint8: "C"
my $outstr = pack("C*", @output);
my $lenstr = length($outstr);
#~ print STDERR "output size: $sizarr; output length: $lenstr\n";
print $outstr;

# end
4

1 回答 1

12

好吧,上面的问题存在一些误解;我为此使用 gnuplot 4.4。首先,请注意我如何认为“ 100x1”中的“ array=100x1:100x1”表示 100 行和 1 列的数据集 - 这与一维数组相同(隐含索引)。然而,事实并非如此;注意help binary array说:

注意:Gnuplot 4.2 版使用语法 array=128x128 而不是
array=(128,128)。较旧的语法现已弃用,但可能
仍然有效 [...]

坐标将由 gnuplot 生成。必须
为数组的每个维度指定一个数字。例如,array=(10,20)表示
底层采样结构是二维的,沿
第一 (x) 维有 10 个点 [..]

因此,即使“ 100x1在概念上可能与 1D 数组相同——只是通过这样编写,我已经将 2D 数据指示为gnuplot例如图像;我希望它的实例化方式不同于 100 的 1D 数组元素,在内部gnuplot)。所以,我应该写“ array=(100):(100)”。请注意,原则上可以为一维删除括号,因此“ array=100:100”也可以 - 除非使用-1(读取到文件末尾)作为维度;然后必须使用括号,否则会发生错误。

 

然后就是多条记录的问题。我很少能找到对这些“多条记录”的引用gnuplot- 有二进制语法减少 [Was: 冗长的讨论...] (gnuplot.devel)

如何丢弃每个文件功能的多条记录。如果要绘制多个大数据集,只需创建多个文件。

一方面,我很高兴他们保留了多记录功能 - 但我当然希望能更好地解释它。我在Gnuplot 中发现的另一条评论,Plot with sum of datasets

... gnuplot(3.6,即)可以在单个记录上绘制数据组合,但不能组合来自多个记录的数据,更不用说多个数据集了。

您必须编写一个预处理器来执行这些计算。

help binary array只是说:

冒号可用于分隔多条记录的维度。
例如,array=25:35表示文件中有两条一维记录

这让我想到,如果我将“ array=(100):(100)”指定为两个(多个)记录,我可以分别在 ausing 0:1using 0:2语句中“访问”这些记录(使用伪列 0 来索引它们的数据)。事实证明,情况并非如此——在我看来,这种多记录规范所允许的唯一设施是能够通过origin和/或skip参数控制它们,但只能在一个 plot 内

谈到记录,注意还有help binary record

此关键字与 具有相同的功能array,具有相同的语法。但是,record会导致 gnuplot 不生成坐标信息。这是针对此类信息可能包含在二进制数据文件的列之一中的情况。

这并没有告诉我太多 - 但我认为这意味着,原则上,如果使用;应该指定伪列 0 来索引数据。record但不应该这样array。因此,以下两个命令是等价的:

plot "bin.dat" binary array=(50) format='%uint8' using 1 with lines
plot "bin.dat" binary record=(50) format='%uint8' using 0:1 with lines

...它将前 50 个样本作为一维数据的第一个(也是唯一一个)记录,其(唯一)数据列格式化为uint8- 并绘制它:

gnuplot-50-samps

注意

  • record使用 just将using 1生成与上述等效命令相同的图像;
  • 但是这样做array0:1从问题 OP 中生成一行!

很遗憾,似乎没有一种明显的方法来调试“列标识符”背后的结构,例如1in using 1,以便以一种或另一种方式确认;但我认为以上意味着:

  • 在 中record1描述了一个 1 列 (50x1) 数据集 - 因此它可以与另一个 1 列索引耦合
  • 在 中array1实际上描述了一个 2 列 (50x2) 数据集,其中第一列是索引 - 这就是为什么当它与另一个索引(伪列 0)耦合时,会生成一行(因为索引伪列与第一列耦合)列 from 1,这也是一个索引 - 即单调上升(或下降)整数的列表

 

然后,注意format参数 -help binary format状态:

[...] 例如,format="%uchar%int%float"
将无符号字符与第一个 using 列关联,将 int 与
第二列关联,将 float 与第三列关联。[...]

现在,这让我想到,如果我想处理两个一维多记录,我必须使用这样的东西:

plot "bin.dat" binary array=(50):(50) format='%uint8%uint8' using 1 with lines

...生成:

gnuplot-2rec-50-samps-resamp

很明显,与正弦重叠的余弦部分在某种程度上是错误的。但更有趣的是,该图仅在 50 个样本中显示了这些函数的整个周期——虽然我们已经明确生成了数据,所以正弦和余弦都有 100 个样本的周期!因此,数据以某种方式重新采样 - 事实证明,问题出在format.

通过指定“ array=(50):(50)”,我们指定了两个(多个)记录,它们是1-D,因此每个记录都有一个(并且只有一个)列。但是,“ format='%uint8%uint8'并不是指两条记录的每一列的格式——它显然是指第二个维度;并且鉴于我们的两个记录是一维的,gnuplot只需从记录中删除所有其他样本。

format='%uint8'因此,我们可以在 plot 命令中只指定一个“ ”:

plot "bin.dat" binary array=(50):(50) format='%uint8' using 1 with lines

...在 50 个样本中只得到半个周期:

gnuplot-2rec-50-samps

......正如预期的那样。但这仍然不能解决记录之间的重叠。

 

重要的是要记住,多个记录似乎总是归因于同一个情节。然后可以使用origin参数调节偏移量;help binary keywords origin状态:

要将数组定位在图形上的其他位置,该origin关键字指示
gnuplot 将数组的左下点定位在
元组指定的点处。元组应该是双倍的plot和三倍的splot

所以,我们可以这样做:

plot "bin.dat" binary array=(50):(50) format='%uint8' origin=(0,0):(50,0) using 1 with lines

...我们可以将其解释为:获取两个连续的 1D 记录,其中它们唯一的维度/列格式为uint8- 并在图上将第一个记录偏移/移动 (0,0),将第二个记录偏移/移动 (50,0 )(+x 方向 50 个单位)。我们现在期望这两个记录将被连接起来,实际上:

gnuplot-2rec-50-samps-conc

...我们现在可以观察我们期望的前 100 个数据样本。

如果我们只是将第二条记录从先前匹配的位置移开一点(例如,还剩 10 个单位),则可以更容易地看出这些记录是“同一情节的一部分”:

plot "bin.dat" binary array=(50):(50) format='%uint8' origin=(0,0):(40,0) using 1 with lines

...在用线条绘制时:

gnuplot-2rec-50-samps-disp

可以立即看出这不再是一个函数,以及原点偏移产生了什么影响。

话虽如此,我们现在可以通过明确指定其中的所有记录及其偏移量来绘制整个数据:

plot "bin.dat" binary array=(4):(100):(100):(30) format='%uint8' origin=(0,0):(4,0):(104,0):(204,0) using 1 with lines

...按预期生成连接的四个记录 - 重新创建整个数据集:

gnuplot-4rec-all

笔记:

  • 使用array=(4):(100):(100):(-1)(for read to end) 生成相同的图像
  • 使用origin=(0,0):(4,0):(104,0)(leave out last) 和 4 条记录array,导致最后 30 个字节从头开始重叠
  • 使用array=(4):(100):(100)(leave out last) 使最后 30 个字节从图中消失(原始记录有 3 个或 4 个)

 

最后,我们看一下skip参数。国家help binary skip

[...] 例如,如果文件在数据区域开始之前包含一个 1024 字节的标头,您可能希望使用
plot '<file_name>' binary skip=1024 ...

这可能有点误导,因为这两个命令:

plot "bin.dat" binary array=(100) format='%uint8' using 1 with lines
plot "bin.dat" binary skip=4 array=(100) format='%uint8' using 1 with lines

...生成相同的情节:

gnuplot-1rec-nooffs

...没有可见的跳跃;但是,如果我们移动skip=4关键字列表的末尾(之前using),命令变为:

plot "bin.dat" binary array=(100) format='%uint8' skip=4 using 1 with lines

...并生成:

gnuplot-1rec-offs

...跳过确实可见的地方。

另请注意help binary skip

如果文件中有多个记录,您可以为每个记录指定一个前导偏移量。例如,要在第一条记录之前跳过 512 字节,在第二条和第三条记录之前跳过 256 字节
plot <file_name> binary record=356:356:356 skip=512:256:256 ...

让我们来说明一下 - 下面的命令:

plot "bin.dat" binary array=(100):(100) format='%uint8' origin=(0,0):(100,0) skip=4 using 1 with lines

...有两个一维记录(我们必须添加origin到偏移量,否则记录将再次重叠),但只有一个跳过 - 这基本上将整个序列向左移动了四个单位:

gnuplot-2rec-跳过

如果我们现在在 skip 中处理这两个字段,如命令中所示:

plot "bin.dat" binary array=(100):(100) format='%uint8' origin=(0,0):(100,0) skip=4:20 using 1 with lines

...我们可以在输出中注意到:

gnuplot-2rec-2skip

...第二条记录已向左移动了 20 个单位 - 为了弥补最后丢失的 20 个单位,其余数据来自下一条记录(在 plot 命令中未解决)。

 

现在我们可以回到最初的问题——“在一个图表上用不同的颜色绘制正弦和余弦数据”。

在此之前,让我们注意每个图有两个数据“函数”,更容易看到origin参数实际上移动了图上的记录;例如,这个命令:

plot "bin.dat" binary array=(100) format='%uint8' origin=(4,0) using 1 with lines, \
"" binary array=(100) format='%uint8' origin=(104,0) using 1 with lines

...结果:

gnuplot-1rec-2func

...其中相同的前 100 个数据样本在图中的两个不同位置呈现(并使用不同的颜色)。

综上所述,很明显:尝试将数据“解析”和“拆分”——例如,通过使用array=(4):(100):(100):(30) ——“记录”,对于每个绘图拥有两个数据“函数”并没有多大帮助(如暗示的那样)通过不同的颜色);仅具有单个数据“功能”。

也就是说,对于两个数据函数的情况,我们只能指定:单个一维记录及其长度array;其(仅)列的格式;以及通过skip- 每个数据“函数”的偏移量:

plot "bin.dat" binary array=(100) format='%uint8' skip=4 using 1 with lines, \
"" binary array=(100) format='%uint8' skip=104 using 1 with lines

...为了获得所需的渲染:

gnuplot-1rec-2func

最后一点 - 我们可以获得完全相同的图表,通过替换binary array-binary record除了而不是using 1,我们应该写using 0:1

plot "bin.dat" binary record=(100) format='%uint8' skip=4 using 0:1 with lines, \ "" binary record=(100) format='%uint8' skip=104 using 0:1 with lines

...即使在这种特殊情况下,using 1也可以。

 

好吧,希望这对某人有所帮助,
干杯!

于 2013-01-23T04:27:50.577 回答