【Gnuradio結合hackrf 通過FSK調製實現文本文件的發送與接收】


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

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章