在gnuplot中绘制具有多个记录的1D二进制数组(uint8)

sda*_*aau 5 gnuplot

我理解这个问题类似于Gnuplot:如何从二进制格式绘制多个时间序列 ; 但是我已经设置了一个略有不同的例子,所以希望发布(自我回答如下).

我正在生成这样的二进制数据(请参阅下面的genbindata.plPerl脚本):

$ perl genbindata.pl > bin.dat
$ du -b bin.dat 
234 bin.dat
Run Code Online (Sandbox Code Playgroud)

此二进制文件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  
Run Code Online (Sandbox Code Playgroud)

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

 

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

我找到了帮助的二进制常规部分(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字节的1D数据(格式化为'%uint8',并在跳过后原点为0,0),然后是100字节的1D数据(一行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]
Run Code Online (Sandbox Code Playgroud)

实际上这确实产生了一个图 - 在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> 
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

sda*_*aau 12

那么,上面的问题有一些误解; 我正在使用gnuplot 4.4.首先,请注意我是如何认为" 100x1"中的" array=100x1:100x1"表示100行和1列的数据集 - 这与1D数组(隐含的索引)相同.但事实并非如此; 注意help binary array说:

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

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

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

 

然后,存在多个记录的问题.我可以找到很少引用这些"多个记录" gnuplot- 有二进制语法减少[是:冗长的讨论...](gnuplot.devel):

如何折腾每个文件功能的多个记录.如果要绘制多个大数据集,只需创建多个文件.

我,我很高兴他们保留了多个记录功能 - 但我确实希望它能得到更好的解释.我在Gnuplot发现的另一个评论,Plot与数据集的总和:

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

你必须编写一个执行这些计算的预处理器.

help binary array只是说:

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

这让我觉得如果我将" array=(100):(100)" 指定为两个(多个)记录,我可以分别在一个using 0:1using 0:2语句中"访问"这些记录(使用伪列0来索引它们的数据).事实证明,情况并非如此 - 在我看来,这种多记录规范所允许的唯一设施是通过origin和/或skip参数控制它们的能力,但仅限于单个图中.

谈到记录,还要注意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个样本作为第一个(也是唯一的)1-D数据记录,其(仅)数据列格式化为uint8- 并绘制它:

gnuplot的-50-SAMPS

注意

  • 这样做recordusing 1将产生相同的图像作为上述的等效命令;
  • 但这样做array0:1将生成的问题OP行!

遗憾的是,似乎没有明显的方法来调试"列标识符"背后的结构,例如1in using 1,以便以这种或那种方式进行确认; 但我认为上述意味着:

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

 

然后,记下format参数 - help binary format状态:

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

现在,这让我觉得如果我想使用两个1-D多个记录,我必须使用这样的东西:

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'" 并不是指两个记录中每列的格式 - 它显然是指第二个维度 ; 鉴于我们的两个记录是1D,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-浓

......我们现在可以观察到我们期望成为数据的前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,所有

注意:

  • 使用array=(4):(100):(100):(-1)(用于读取结束)生成相同的图像
  • 使用origin=(0,0):(4,0):(104,0)(遗漏最后)四个记录array,导致最后30个字节从头开始重叠
  • 使用array=(4):(100):(100)(遗漏最后)使最后30个字节从绘图中消失(原点中有三个或四个记录)

 

最后,让我们看一下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取舍

......确实,跳过的位置是可见的.

另请注意help binary skip:

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

让我们举例说明 - 以下命令:

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

...有两个1-D记录(我们必须添加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个单位,剩下的数据来自,否则,下一个记录(在绘图命令中没有解决).

 

现在我们可以回到最初的问题 - "在单个图表上绘制单独颜色的正弦和余弦数据".

在此之前,让我们注意到,每个绘图使用两个数据"函数",可以更容易地看到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) - 进入"记录",对于每个情节具有两个数据"功能" 将无助于我们(如暗示的那样)用不同的颜色); 只有一个数据"功能".

也就是说,对于两个数据函数的情况,我们只能指定:单个1D记录及其长度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也会起作用.

 

嗯,希望这有助于某人,
干杯!