记: 对于SCPI指令以及相同类型指令解析器的指令压缩方式

22 篇文章 1 订阅
11 篇文章 1 订阅

0x10 前言

SCPI是一个对人或者说用户十分友好的语言,采用了人性化的抽象与对于用户很友善的组成方式。

但是对于某些机器的设计就会很难受,而且当前的机器会在日后的不停更新导致当前的程序越来越呈现一种指数级别的裂变。这种裂变是冗余的、灾难性的,因此需要一个简单的压缩方式或者说压缩算法进行数据的去冗余,提炼出干练的短句。

阅读本文之前,您需要掌握的技能有:

技能名称技能熟练度技能教程链接
C语言熟悉暂无
数据结构熟悉暂无

0x20 简单介绍

这是一个简单的SCPI指令:

A:B:C:D:E

可以看到一个简单的层级结构,A为第一级,一般代表一个大类。B为一个中间点,是一个主要的选项,C、D、E为当前的选择支类,他们可能并不是很重要,但是可能很多指令存在。

这些指令也有可能是可隐藏的,比如:

[A:]B:C[:D]:E

这种指令下,B:C:EA:B:C:D:EA:B:C:EB:C:D:E都是对的,只要保证[]以外的全部都有即可。

而一般的指令都会有两个格式,写入与获取。

写入就是很简单的加参数,读取更简单,一般直接在指令后加上?即可。

所以一个标准的指令一般有两类:

  1. 加参数的写入指令
  2. 加?的读取指令,有的指令会加参数读取。

0x30 基本思想

可以看到,这边的指令具有明显的冗余位置:

当前的指令必定会有读取和写入,而他们大概率是长得一样的,这种压缩就取决于当前是否需要了,这里仅讲述个人认为较为简单的压缩思想:去重查找。

去重的核心就是找到重复的数据。然后把当前的数据使用特殊的方式映射到表中,随后可以在需要的地方进行还原。

这里因为单板的计算能力有限,所以笔者使用了较简单的查表法进行还原,但是采样的方式较为麻烦。

0x40 查重

当前的数据存在及其严重的数据冗余,这里使用很简单的比对方式:主要步骤为数据截取、数据比对、数据连接。

笔者这里选择的是双向链表的形式,一方面是锻炼自己的数据操作能力与数据架构能力,一方面也是根据当前不定长度与不定个数的数据结构的较为简单的生成。

这里的数据仅仅需要可用的数据,也就是忽略了一切标点符号的连续字符。每段字符串都是一节可用的数据。并且为了单板的拼接方便,这里还使用了数据ID的标记针对当前的字符串做出专有的定义。这样在最后只需要生成以ID从小到大排列的顺序生成一个数组即可,这样就可以直接根据ID拼接数组形成最后的指令。

根据上面的要求就可以得到一个明确的基本数据结构:

typedef struct
{
    
    unsigned int ID;
    char* String;
    
    
}

这个数据还缺了上下的链表,于是需要这样:

typedef struct S_STRING
{
    struct S_STRING* prev;
    struct S_STRING* next;
    unsigned int ID;
    char* String;   
    
}S_STRING_NODE;

这个就是一个基本的双向链表,传输的指针也不需要必须要是特定的字符串,只需要一个数据的指针即可。所以还需要一个当前的数据长度的指示位。

typedef struct S_STRING
{
    struct S_STRING* prev;
    struct S_STRING* next;
    unsigned int ID;
    char* String;  
    unsigned int length;
    
}S_STRING_NODE;

这就是一个节点。

有了一个节点,就可以向里面填充数据。数据的结构虽然是严格意义上的双向,但是还是需要一个头作为笔者编程时调试的锚点。也好判断当前的数据结构是否正确。于是笔者定义了一个作为锚点的根数据,随后的数据全都是衔尾蛇一般的双向数据链表。

假设输入的数据是这样子的

SYSTem:REMote
SYSTem:RWLock
SYSTem:GreenDream

这里数据最后的输出结果就是:

GreenDream SYSTem REMote RWLock ROOT

这个样子。(这个图已经是笔者能够找到的最好用的图片了)

这样就会将原本46字符(算上占位符)的字符减小变成约32个字符。压缩了大约70%。

随着数据越加越多,对于原本的数据压缩甚至可以做到10%(笔者试验了某功能较多的机器,各类指令约有800条,原指令约为22k,压缩完成后的数据约为2.2~2.5K)

就其根本是因为这种层级指令组合本身就不适合使用简单粗暴的方式进行数据检索。更应该是使用类似字典查表法进行依次比对。

0x50 二次映射

前文为了降低阅读量(笔者最近太忙以至于并没时间写关于SCPI的构成),就省略了SCPI中,指令的基本构成法则。

下面介绍下基本的构成规则:

  1. 指令主要分为必须要的指令、可省略的指令。
  2. 指令方向有设置与读取。
  3. 指令层级以:结尾。

于是出现了四种构成指令层级的方式:?[::]:

而具体的指令格式则为当前需要的要求组成,就像上文那样的组成即可。

现在已经有了一个完整的数据去重的链表。下面需要做的就是把这个链表做映射,并将映射的数据导出。

导出的前提是有一个结构化的表格类似的数据结构。笔者自己做了一个类似于Hash Table的数据结构,总体的格式类似于思维导图的形式。

在这里插入图片描述

可以看出来,总体是一个{形式的一个数据结构。因此也就出现了两个必须要解决的问题:数据格式的定义,数据格式的拼接。

可以看到,这两个问题属于一个问题:如何优雅的利用数据的解析形式,完成对于整个映射的构造。

上图可以看到,整个数据结构有三类节点:根节点、指令头部节点、跟随的后续指令节点

根节点仅具备区域边界的指向性,也就是说只负责指定当前数据结构的区间的第一个数据与最后一个数据。从而使得整个指令头部形成一个衔尾蛇的循环结构。而所有的指令头部节点全部都会指向根节点,以保证当前节点可以迅速回到根,从而进入其他的节点。从图片上可以看出,头部节点具有四个方向,而整体的数据构成了两个双向链表与一个循环结构。也就是说当前的节点需要四个指针向量。

typedef struct S_1
{
    struct S_1* prev;
    struct S_1* next;
    struct S_1* supper;
    struct S_1* child;
    
}S_S;

这就是两个指针表示前后,两个指针表示上下。随后就需要ID了。

typedef struct S_1
{
    struct S_1* prev;
    struct S_1* next;
    struct S_1* supper;
    struct S_1* child;
    
    unsigned int Id;
    
}S_S;

这个时候还少了最关键的指令的层级表示。也就是对于字符串的?[::]:
因为只有四个状态,正好就是两个比特位。这样就可以得到一个数据了。
考虑到北京大学图书馆-牛津词典检索的记录单词的个数,一般来说20比特的数据就可以直接得到几乎全部的英语单词量了。但是一般人都不会用这么多的单词构架指令数。所以当前的数据结构暂时可以只使用32比特的数据定义,其中前拆出2位数据作为层级表示的定义,剩下的作为检索的ID使用。


typedef enum
{
	NORMAL,
	LEFT,
	RIGHT,
	RETURN
}E_S;

typedef union
{
	E_S special:2;
	unsigned id:14;
}U_S

typedef struct S_1
{
    struct S_1* prev;
    struct S_1* next;
    struct S_1* supper;
    struct S_1* child;
    
    U_S Point;
    
}S_S;

这样就形成了一个基本的数据结构。
笔者的能力有限,也只有结合当前的结构,在某些情况下选择性的更新某些数据指针。例如

  1. 在根节点中,上下分别指向根节点和空闲。而前后分别指向第一个和最后一个节点。
  2. 头部节点中,上下则指向根节点与跟随节点,左右分别指向相对于现在的先后添加的头部节点。
  3. 而跟随节点仅包含前后结构,最前面的跟随节点指向头部与随后的跟随节点
  4. 后面的跟随节点则前后都是跟随节点。

总体上的组合就是这样组合,虽然实际上拥有四个层级,但是数学告诉人们,只需要三道横线就可以画出四个区域。

0x60 组合对比

笔者对当前的压缩方式计算了一下,对于选择的样本:860个指令,一个真实的机器指令集,约22000字符。

压缩前字符22000,约为20k。

压缩后的的单词约为270个,仅占用有效9位有效ID字节。而所有的单词占用的字符约为2000,也就是2k。

经过映射的数据节点约为3800个,每个数据节点的有效数据约为2byte,也就是约为7kb。

随后需要合成的临时数据空间暂时定义为300b,这个区间足可以定义绝大多数的指令。而且仅占用RAM不会占用ROM。可以随时动态申请。

也就是大约需要10KB的实际占用空间。也就压缩了约50%的实际空间。

当然,如果能够再利用一个字节将读取与写入的指令绑定会得到更大的压缩效率(保守估计约为80%左右),但是对于解析器的要求很大,必须要在解析器构建之前就进行一定的前置适配。

以上。

更多

本文首发自 记: 对于SCPI指令以及相同类型指令解析器的指令压缩方式,更多文章可进入我的博客详查。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SCPI程控仪标准命令,六十年代的自动测试系统{ATS) 没有 标准的接121和程序、接口电路}{{设计者自行考 虑, 用汇编语言编程。那时微型it 算机尚未问 世、接口总线标准难以确定。结果是测量仪 之间的连接和通信不能兼容、编程结果不能互 换和共享,对设计者和使用者都不方便: 七十年代徽处理的普及和智能仪的出 现,使IEEE 488.1总线接口标准获得公-^. 对 于自动测试系统的影响非常深远。到目前为止, 约 分之八十的ATS仍使用正E E 488.1总线。 总线控制大都由微型计算机担当.BA S Ic 语青作为通用的编程手段,代替编程效率较低 的汇编语言。换句话说,ATs的接VI总线在硬 件和遥控信息两方面作出明确规定、ATS本身 也获得迅速的发展= 八十年代ATS 要求对接VI总线有进一步 的提高,于是IEEE48 8.1升级至1EEE488.2,对 数据格式、状态报告、命令设置、误差处理、 件命令等更高的消息层次作出规定,使A、 B.C三胫的消息通信都有规叮循(见图l】。 但是程控仪的最高层D 【件消息层)仍然由 仪设计者自行规定。同时微处理也迅速从 字长8位发展到16位、32位。达到以三个l0M 作为表征的水平(即时钟10MH z,内存lcMB. 运算速度10MIP), 数据传输速度和运算能力 显著提高。编程语青除BAsIc外,也使用更 高级的c语言。并出现专供ATS编程用的机 辅设计工具。 由于超大规模集成电路使测量仪的体积 缩小, 出现单片AsIC电路的仪, 或者一个 机箱式仪由一一块插卡来代替:以32位的V ME微机E业总线为雎础的程控仪V xI总线随 之诞生,V xI对软件和语言并无规定,实际上 执fflEEE 48 8.2协议。V x I总线为九十年代 A T s的小) 化提供更良§r的硬件条件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GreenDreamer

如果帮到了你,还望请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值