Gnuradio结合hackrf 通过FSK调制实现文本文件的发送与接收
一、实现目标
1、将我们要发送的文件封装成帧;
2、通过FSK调制与解调实现文件的传输;
3、将接受到的文件进行非实时的采样处理还原我们发送的文本文件;
二、实现过程
1、将我们要发送的文本文件封装成帧
(1)首先我们要选择文本文件,例如send.txt我们在文件中输入我们要发送的字符串,在本次实验中我输入的是“Hello World!Xiaoming”,输入完后保存文件并退出。
(2) 利用自己编写的Python程序data_send.py将我们的要发送的文本文件send.txt封装成简单的数据帧
data_send.py的截图
①首先在帧头写入同步序列
代码部分:
serial =[1,0,1,1,0,1,0,0]
serial=serial*2
for i inserial:
f.write(chr(i))
通过代码可以看出,将[1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0]作为我们的同步序列(为了后面的帧同步),将同步序列以字节的形式写入code.txt文件中。
②接着写入数据的大小长度(以字节为单位)
代码部分:
str1 =intTo2Str(length,8)
for i in range(8):
temp = str1[i]
temp = int(temp)
f.write(chr(temp))
计算我们要发送文本文件的大小,此处我用一个字节表示文本的字节数,即在本次试验中我们最多能够发送256字节(稍稍改变代码,可实现更多字节的传输),本次实验的为20字节,并以追加的方式写入code.txt文件中。
③写入数据
源代码:
for i in range(length):
str1 = intTo2Str(ord(send[i]),8)
for i in range(8):
temp = str1[i]
temp = int(temp)
f.write(chr(temp))
通过循环,我们将要发送的字符串,依次转换为字节,并追加写入code.txt文件中。
④写入结束标志
源代码:
serial =[1,1,1,1,1,1,1,1]
for i inserial:
f.write(chr(i))
f.close()
我们通过数据长度就可以确定数据的长度,所以结束标志并不是为了判断数据段的结束。我们知道我们的帧会被循环的发送出去,而我们的结束标志则是为了避免产生相同的同步序列。
⑤运行data_read.py,我们打开code.txt,因为我们发现会出现乱码,就说明我们基本正确。我们写入code.txt是0或1的字节,用数字的方式写入文件。
2、将我们要发送的文本通过hackrf发送出去
(1)发送端的grc流程图
流程图介绍:
①通过File source模块读取我们的code.txt中以保存的数据,因为我们的0,1是以字节的方式存入,所以File source模块数据类型参数选择Byte,还有记住选择code.txt的文件路径。我们可以从Vector wave看出读取出来的是0,1序列。
②Char to Float模块将字节数据类型转变为Float,方便后面的数据操作,例如乘除。
③接着我们把0,1变为-0.5和0.5,接着在乘以2,即-1,1,接着在每个数字后年插入0,达到的效果是从[0,1,,,]序列变为[-1,0,1,0,,,,,]等。然后通过Repeat进行插值,即从[-1,0,,,]变为[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,,,,,],通过这一系列的变化,达到的效果就是把读取出来的[0,1]序列用双极性方波,即从vector wave 变为Bipolar wav。(选择双极性方波的原因是方便的抽样判决)。
④通过,通过FractionalInterpolator 模块,稍稍改变原有的波形的形状,使原来的波形更容易加载到载波上。
⑤将稍稍变化后的方波通过WBFM Transmit模块完成调制。当我们的将调制后的波形放大看,发现调制后的波形,不怎么的好,于是我们就可以利用Rational Resampler模块进行插入重采样,可以使原来的波形变得更好(更光滑)。(详细的效果参考实验1的效果图)。
⑥然后通过HackRf发送出去,这里的我们用的100M,如果效果不好,可以自己设置。
发送端效果图
(2)接收端的grc流程图
流程图介绍:
①通过osmocom Sink模块接收我们发送的的信息,其Freqency参数同发送端一样,同为100M,还有我们还以根据需要设置RF Gain,IF Gain,BBGain的值,来调节我们接收到波形的幅度的大小,本次实现依次设置的是12,10,10。
②首先还是通过波形进行RationalResampler模块进行抽取重采样,主要是与发送的端的Rational Resampler模块想对应,是发送信息和接受的信息的速率一样。
③接着我们通过一个低通滤波器模块Low PassFilter滤除高频部分,是波形变得平坦,有利于后面的抽样判决。
④接着我们就通过Float To Int 模块实现将数据进行量化,大于0.5取1,介于-0.5到0.5之间取0,小于-0.5取-1。(注意此处我们接收到的信号幅度在-1~1左右)
⑤ 接着我们将数据保存到文件中去,这里自己设置文件的保存路径,此次试验我们保存的文件的文件名是receive.txt。
接收端效果图
(3) 将保存的文件转化为我们能操作的数据类型
使用的命令是:
gr_plot_int2 -B20000 /home/…./receive.txt
此处gr_plot_int2是一个编写python文件,由于代码太多,这里不再节后,详细的代码看后面的附录。
化后我们保存到kk.txt这个文件中,打开文件我们可以看出,全是1,0,-1组成的序列。
(4) 从kk.txt文件中解码出我们发送的文件信息。
源代码如下:
①首先读取出文件,稍作处理,将数据加载到数组中去。
代码部分:
str_arry =[]
data_arry =[]
line =f.readline()
sign = line
line =line.split(', ')
printstr(line)
for str1 inline:
str_arry.append(str1)
while sign!='':
line = f.readline()
sign = line
line = line.split(', ')
for str1 in line:
str_arry.append(str1)
f.close()
str_arry =str_arry[1:-2]
data_arry =str_arry
n =len(str_arry)
for i inrange(n):
if data_arry[i] == '1':
data_arry[i] = 1
if data_arry[i] == '0':
data_arry[i] = 0
if data_arry[i] == '-1':
data_arry[i] = -1
过代码可以看出,我们是将0,1,-1一个字符串数组,变为数字形式的数字数组,这样可供我们后面进一步的分析处理。
②接着就是抽样判决
代码部分:
data = []
data.append(data_arry[0])
for i in range(1,n):
ifdata_arry[i] != data[-1]:
data.append(data_arry[i])
data = data[20:-1]
此处我们通过判断数据跳变来确定采样时刻,这就是就是利用双极性方波特点,在每个周期内电平会跳变。
③确定同步序列
代码部分:
serialnumber= [1,0,-1,0,1,0,1,0,-1,0,1,0,-1,0,-1,0]
serialnumber= serialnumber*2
m =len(serialnumber)
index = 0
for i inrange(n-m):
sign = 0
for j in range(m):
if data[i+j] != serialnumber[j]:
sign = 1
if sign == 0:
index = i+32
break
print'i='+str(index)
data =data[index:-1]
print'ss'+str(len(data))
new_data =[]
for i inrange(len(data)):
if i%2==0:
new_data.append(data[i])
for i inrange(len(new_data)):
if new_data[i] == -1:
new_data[i] = 0
通过同发送端的同步序列对比,找到接收文件中的同步序列,即确定帧的开始地方。
④获取数据包的长度
代码部分:
sizeofdata = ''
for i in range(8):
sizeofdata = sizeofdata+str(new_data[i])
sizeofdata =int(sizeofdata,2)
print str(sizeofdata)
new_data =new_data[8:-1]
上面确定好同步序列,知道帧的起始接着8位数据就是我们发送的字符串的长度,从而确定后面数据包的长度。
⑤获取数据包并还原为字符串
代码部分:
rec_string = ''
for i in range(sizeofdata):
index= i*8
temp=''
fori in range(8):
temp= temp+str(new_data[index+i])
rec_string= rec_string+chr(int(temp,2))
print 'receive:'+rec_string
f = open(r'/home/cyp/myfile/decode.txt','wb')
f.write(rec_string)
上面确定数据包的长度长度,我们直接读取指定长度的数据部分,并还原为字符串形式,保存到decode.txt文件中。
⑥使用命令
在终端中运行:./data_read.py
将解码获取的字符串信息保存到decode.txt文件中。通过对比发现同发送的文件字符串一样,证明我们正确的实现文本文件的传输。
总结
通过这次实本文件的发送与接收,我们对hackrf和gnuradio有了一个更深的理解,同时让我们切实体会到帧结构,同步序列,调制与解调的原理,让我们学到的理论知识在实践中得以验证。
展望:通过对代码进一步的修改,帧结构的进一步完善,就可以实现图片,甚至视频的传送。
附录:
gr_plot_int2文件的源代码如下:
#!/usr/bin/envpython
#
#Copyright 2007,2008,2011 Free Software Foundation, Inc.
#
#This file is part of GNU Radio
#
#GNU Radio is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 3, or (at your option)
#any later version.
#
#GNU Radio is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with GNU Radio; see the file COPYING. If not, write to
#the Free Software Foundation, Inc., 51 Franklin Street,
#Boston, MA 02110-1301, USA.
#
"""
Utilityto help plotting data from files.
"""
try:
import scipy
exceptImportError:
print "Please install SciPy to runthis script (http://www.scipy.org/)"
raise SystemExit, 1
try:
from pylab import *
exceptImportError:
print "Please install Matplotlib torun this script (http://matplotlib.sourceforge.net/)"
raise SystemExit, 1
fromoptparse import OptionParser
classplot_data:
def __init__(self, datatype, filenames,options):
self.hfile = list()
self.legend_text = list()
for f in filenames:
self.hfile.append(open(f,"r"))
self.legend_text.append(f)
self.block_length = options.block
self.start = options.start
self.sample_rate = options.sample_rate
self.datatype = datatype
self.sizeof_data =datatype().nbytes # number of bytesper sample in file
self.axis_font_size = 16
self.label_font_size = 18
self.title_font_size = 20
self.text_size = 22
# Setup PLOT
self.fig = figure(1, figsize=(16, 9),facecolor='w')
rcParams['xtick.labelsize'] =self.axis_font_size
rcParams['ytick.labelsize'] =self.axis_font_size
self.text_file_pos = figtext(0.10,0.88, "File Position: ", weight="heavy",size=self.text_size)
self.text_block = figtext(0.40, 0.88, ("Block Size:%d" % self.block_length),
weight="heavy", size=self.text_size)
self.text_sr = figtext(0.60, 0.88, ("SampleRate: %.2f" % self.sample_rate),
weight="heavy", size=self.text_size)
self.make_plots()
self.button_left_axes =self.fig.add_axes([0.45, 0.01, 0.05, 0.05], frameon=True)
self.button_left =Button(self.button_left_axes, "<")
self.button_left_callback =self.button_left.on_clicked(self.button_left_click)
self.button_right_axes =self.fig.add_axes([0.50, 0.01, 0.05, 0.05], frameon=True)
self.button_right =Button(self.button_right_axes, ">")
self.button_right_callback =self.button_right.on_clicked(self.button_right_click)
self.xlim = self.sp_f.get_xlim()
print 'start!'
self.manager = get_current_fig_manager()
connect('key_press_event', self.click)
show()
def get_data(self, hfile):
self.text_file_pos.set_text("FilePosition: %d" % (hfile.tell()//self.sizeof_data))
try:
f = scipy.fromfile(hfile,dtype=self.datatype, count=self.block_length)
except MemoryError:
print "End of File"
else:
self.f = scipy.array(f)
self.time =scipy.array([i*(1/self.sample_rate) for i in range(len(self.f))])
fhh2 = open(r'/home/cyp/myfile/kk.txt', 'w')
print str(len(self.f))
data = []
for j in range(len(self.f)):
data.append((self.f[j]))
fhh2.write(str(data))
fhh2.close()
def make_plots(self):
self.sp_f = self.fig.add_subplot(2,1,1,position=[0.075, 0.2, 0.875, 0.6])
self.sp_f.set_title(("Amplitude"),fontsize=self.title_font_size, fontweight="bold")
self.sp_f.set_xlabel("Time(s)", fontsize=self.label_font_size, fontweight="bold")
self.sp_f.set_ylabel("Amplitude(V)", fontsize=self.label_font_size, fontweight="bold")
self.plot_f = list()
maxval = -1e12
minval = 1e12
for hf in self.hfile:
# if specified on the command-line,set file pointer
hf.seek(self.sizeof_data*self.start, 1)
self.get_data(hf)
# Subplot for real and imaginaryparts of signal
self.plot_f += plot(self.time, self.f,'o-')
maxval = max(maxval, self.f.max())
minval = min(minval, self.f.min())
self.sp_f.set_ylim([1.5*minval,1.5*maxval])
self.leg =self.sp_f.legend(self.plot_f, self.legend_text)
draw()
def update_plots(self):
maxval = -1e12
minval = 1e12
for hf,p inzip(self.hfile,self.plot_f):
self.get_data(hf)
p.set_data([self.time, self.f])
maxval = max(maxval, self.f.max())
minval = min(minval, self.f.min())
self.sp_f.set_ylim([1.5*minval,1.5*maxval])
draw()
def click(self, event):
forward_valid_keys = [" ","down", "right"]
backward_valid_keys = ["up","left"]
if(find(event.key,forward_valid_keys)):
self.step_forward()
elif(find(event.key,backward_valid_keys)):
self.step_backward()
def button_left_click(self, event):
self.step_backward()
def button_right_click(self, event):
self.step_forward()
def step_forward(self):
self.update_plots()
def step_backward(self):
for hf in self.hfile:
# Step back in file position
if(hf.tell() >=2*self.sizeof_data*self.block_length ):
hf.seek(-2*self.sizeof_data*self.block_length, 1)
else:
hf.seek(-hf.tell(),1)
self.update_plots()
deffind(item_in, list_search):
try:
return list_search.index(item_in) != None
except ValueError:
return False
try:
import scipy
exceptImportError:
print "Please install SciPy to runthis script (http://www.scipy.org/)"
raise SystemExit, 1
fromoptparse import OptionParser
defmain():
usage="%prog: [options] input_filenames"
description = "Takes a GNU Radiointeger binary file and displays the samples versus time. You can set the blocksize to specify how many points to read in at a time and the start position inthe file. By default, the system assumes a sample rate of 1, so in time, eachsample is plotted versus the sample number. To set a true time axis, set thesample rate (-R or --sample-rate) to the sample rate used when capturing thesamples."
parser =OptionParser(conflict_handler="resolve", usage=usage,description=description)
parser.add_option("-B","--block", type="int", default=1000,
help="Specify theblock size [default=%default]")
parser.add_option("-s","--start", type="int", default=0,
help="Specify where to start in the file[default=%default]")
parser.add_option("-R","--sample-rate", type="float", default=1.0,
help="Set thesampler rate of the data [default=%default]")
(options, args) = parser.parse_args ()
if len(args) < 1:
parser.print_help()
raise SystemExit, 1
filenames = args
datatype=scipy.int32
dc = plot_data(datatype, filenames,options)
if__name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass