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