(有点乱,之后会有整理)
最近在用写一套gnuradio的OOT模块,主要用来进行BLE嗅探的,github上有了一些工具,可是他们并没有很好的模块化,于是打算自己写一个,这样以后做一些其他的项目,模块可以在grc流图里直接用,复用性会好很多。
我在github上找到了一个项目叫做BLE_dump,我参考了他的解调方式,事实证明这种解调方式的效果非常好,具体的细节可以参考我博客中转发的另外一篇文章。
先总的来看一下流图,目前可以输出二进制的BLE包,还没有解扰
首先就是bit规则,这点在蓝牙官方手册里说得并不太清晰(起码我没有看明白),bit 规则讲的是bit流是以何种顺序被发射出去,这是序列检查的关键,经过我很多天的尝试,大概确定了下来。以比较常见的广播信道为例。
在表述中,使用0x8E89BED6表示广播信道的Access Address地址,这是标准广播信道的部分数据包格式。于是就有了第一个模块——find_pack_by_preamble。检测前导码的时候,我没有采用上文中BLE_dump的方式,他的方式是先把二进制流转换为16进制,之后检查0xAA,但是这样可能会有同步的问题,前导码一半在一个16进制数里,另一半在后边。我使用了一个有376位的数组,每次从后往前位移,例如第375位的数,他是刚输入进来的,下一次被移动到375,然后374,最后到0,然后被丢弃。我检查从0位到7位,这8个bit是01010101,那么就是一个广播信道的前导码。按照BLE规则,无论什么信道,要保证9个的交替bit,所以前9个都可以作为识别。检查通过的话,就把这376位压缩为47个16进制数,交给下一个模块分析。
因为经过这个块之后,数据会变成异步,所以在使用gr_modtool创建的时候,选择的sink模板,这样是没有输出端口的。那么数据该如何异步输出呢,我使用了 Async Message 作为输出数据流。这里的输出端口的声明和常规的例子是不一样的,需要按照如下的规则声明
我还没有太搞懂set_output_multiple的作用,可能是缓冲区的设置,因为我把它删除之后,数据流会时不时发生异常,下面一句就是声明异步输出端口的其中pmt::mp(“out”)可以理解成端口的ID号码。
同时,对xml文件也要有所改动,如下图
手动添加了(makexml不可以)一个out端口,数据格式为message,也就是grc中灰色的小框。
下一个模块我用python写的,主要是根据access address对上级block过来的数据包再次筛选,首先,因为是接收异步数据,要多import一些东西
在获取数据之前,要声明一些东西
Message_port_register_in(pmt.inter(‘in’))是声明这个模块接收异步数据,下一个,是指示回调函数,第一个参数要pmt.inter(‘in’)保持一致,第二个参数是用来处理数据包的函数名,也就是相当于work函数,后面还有一个out端口,那是之后的任务,这里就把它留在那。
数据的获取和处理如下:
函数注意要和上文所提保持一致,上级模块发送过来的数据就在第二个参数里。数据传入之后,首先对数据调用pmt::cdr,这个函数在gnuradio-mastergnuradio-runtimelibpmtpmt.cc中
查了一下,好像是有关梳理类型的转换和继承什么的,我不太熟悉,我这么写的原因是看很多模块都是这么写的,姑且先背下来,如果有哪位师傅知道请留言帮助补充,谢谢。
下面的函数也在gnuradio-mastergnuradio-runtimelibpmtpmt.cc中,是一个类型检查。之后就是对广播信道的地址进行检查,
符合条件的,转变成字符串,去除头部的0x和尾部的L,并输出。
(2017,7,23)之后,我们开始解扰码。
我们现在先看一下解扰之后的效果:
我们先看看扰码的位置:
再读取了前导码之后和assess address之后,就是PDU包,PDU包整体是加扰的,我们先看看解扰码的序列生成:
这个生成器的初始bit详见蓝牙官方协议vol6的3.2,我写了一个简单的扰码生成序列,写的有点丑
他会生成一个二维数组,再解扰的时候,我直接调用这个二维数组作为查找表。
比起动态生成扰码序列,把值域和定义域都确定的函数做成查找表会节约很多耗时,我这里,大约可以节约0.3ms,占这个模块整体耗时的百分之60。
接下来,我们看看解完扰码之后的bit序列,下面是PDU包的定义:
前4bit是PDU类别,我们可以通过蓝牙官方协议确定这个包的种类。之前嗅探的是广播信道的数据包,所以很多是0000,这个代表是ADV_IND数据包,之后的RFU是保留位,为以后的预留,现在使用0代替,也就是说上图的第一个包,是有错的,之后是TxAdd和RxAdd,之后再说,之后的Length是表示长度,比如上文的第二个包,length是000110,LSB下是0b11000,就是24,表示整个PDU包不算PDU包头,有24个octets。
(2017,7,25)python模块异步输出
上面,我们已经留出了out端口用来输出,可是怎么进行输出呢?
比如,在我的流图中after_dewhite_str是我所需要从这个块输出的数据,类型位string,那么我需要首先
把它变成一个array类型,之后
after_dewhite_str就被通过异步的方式从模块中发送出去了
(2017,7,30)参数化函数
我们先来看一下一个可以被py脚本import导入的模块的流图应该是一个什么样子
在BLE中,有两个变量是很重要的,他们是Access Address和channel(信道编号),在链接过程中,Access Address会改变,而在跳频中,channel也会改变,所以我们需要在程序中留出动态设置他们的接口。
在流图中看起来,就是这个样子。
为了动态设置这两个参数,我们需要使用gr_modtool创建模块的时候对参数进行输入,
这样会在对这两个函数提供底层的支持,如果直接在已经创建好了的模块中修改,则会出现很多错误,很麻烦。
可以看到,_init_中在self之后多了两个参数,就是我们需要输入的,gr_modtool已经完成了底层的支持,在这里,我的channel是int型,而accaddr是string,这是为了在流图外更好的调用,所以进入模块之后要进行一些格式化,把最后的写入self.XXX,在后文就可以使用了。
在grc文件夹下的对应xml文件也需要进行修改,添加两个param标签。这样就完成了模块中的参数化。
接下来说一个小窍门,流图是没有办法完成跳频之类的任务的,它毕竟不如py脚本灵活,我们实际上是可以把流图作为一个py模块import进一个py脚本中的,这个时候,在使用流图生成py文件的时候,就需要大量的使用variable模块,下面是我给accaddr定义的variable模块。
ID是你需要代表的参数,value是值,但是,为什么我定义的accaddr是一个string,但是这里他是一int呢,
因为这个模块不支持string格式,支持string的我也没找到,所以只好用这种办法(逃。。。。)
好处是什么呢?
这样,我们就可以使用import XXX 来引入我们的流图,之后使用XXX.set_accaddr来在py脚本中动态改变这个值了,很方便。
现在我们已经可以动态改变流图的参数了,那么怎么获取数据呢,这时ZMQ异步消息机制就变得十分有用。
我们可以通过这种方式把grc流图中的数据送出来,进入消息队列。之后在py脚本中
先import gr_ble和zmq,之后在下面
进行一些设置,最后,函数socket.recv()的返回值就是流图中的数据了。
这里在使用的时候需要注意一下,由于channel是int,所以不能为空,如果空着流图会报错,而accaddr是string,控string是允许的,如果不填,流图不会报错,可是逻辑上是行不通的,运行会报错。使用其他模块的时候也要注意这一点。
(2017,8,3)模块动态参数支持
我之前以为模块会默认支持动态参数,可是在动态信道号设置的时候发现了解扰是有问题的,也就是跳频跳过去了,但是解扰信道号码没过去,换句话说,就是osm的源模块支持了动态参数而我自己的模块debugsink不支持,但是,这样我至少可以确定动态参数机制在grc中是支持的。
一般来说,动态参数有两种方式,第一是使用异步消息输入代替模块参数,就像我的grc流图中第二个模块那样,这样的好处是可以远程调用,但是我需要完成的是跳频,对之间精度要求比较高,我不知道zmq消息的时间精度,所以我使用了第二种方式。
gnuradio中提供了针对模块的回调机制,就是当发现参数被外部改变的时候,调用一个回调函数,在回调函数中完成对参数的动态设置。下面我说一下步骤。
首先,要在模块对应的xml文件中,在<make>块的下面,加入<callback>模块。(如果写在<make>上面,编译流图里就没这个模块,所以要注意一下顺序)
之后,在对应模块中要写这个两个回调函数,这两个回调函数有俩个参数(对于python模块),第一个是self,第二个是变量。
这样就完成了模块对动态参数的支持。对于C语言模块,gnuradio中有很多原生的模块都支持,可以参阅他们的代码。
(随时更新修改,爬虫网站上都是历史版本 http://www.cnblogs.com/backahasten/ 随时查看更新)