Saturday, August 29, 2015

Processing data out of GNURadio

While working on software defined radio (SDR), I have always worked on GNURadio software which provide a graphical interface. I have to drag and drop various blocks to create flow graphs which perform the functionality I need. It's time to escape the barriers of GNURadio and do further thing out of it. Finally I found the way to do it. We can start with GNURadio flow graphs and transfer data from it to our own python scripts where we have the freedom to do whatever we want with our program codes. I found some good resources from where I learnt it and those things are mentioned under the references section.

First of all to try these things, we need to have installed some software. GNURadio should be there as obvious. Additionally we need a python plotting library which we will be using. Install it as follows.

sudo apt-get install python-matplotlib

Let's clearly understand what we are going to do now. When we run a flow graph created using GNURadio, it will acquire the data from wherever we specified and will process it inside. Resulting data can be visualized as different plots provided as blocks within the flow graph or sometimes we can save data in to a file. Every kind of processing I have done so far are within the flow graph. Now, what we are going to achieve is creating a flow graph which has some special blocks. Instead of running the flow graph straightforwardly, we just generate a python script using GNURadio that represent the flow graph. Then we write a our own python program which will use that GNURadio generated python script as a module to acquire data and then do further processing of data inside our own python script.

It's time for action. Open gnuradio-companion tool and create the following flow graph. It will acquire data from a HackRF device and apply a fast fourier transform (FFT) operation on the data stream. Finally it turns that FFT data in to message block which are ready to be sent out of the flow graph though a special block. I saved this flow graph as fft_data.grc and clicked on the 'generate flow graph' button to generate the python script which implements this flow graph is code. It generates the script called top_block.py automatically.


Once this is done,  we have a python script which will acquire data and provide us FFT data. Let's use it in a our own python script. For this purpose, create a new python program with the name fft_plotter.py and add the content shown below. It should be understandable that we have two functions there. First one draws the FFT for the considered frequencies while the second one draws the variation of signal power of a particular frequency over time. Depending on what we want to plot, we can uncomment the relevant line.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import grcconvert
import struct
grcconvert.main("top_block.py")
import top_block
import matplotlib.pyplot as plot
import os
from collections import deque

def plot_fft(with_this):

 #Asanka: trying to play with this
 os.system('clear')
 print "------------------------------"
 print "length       =", len(with_this)
 print "start point  =", with_this[0][0], "dB"
 print "center point =", with_this[len(with_this)/2][0], "dB"
 print "end point    =", with_this[len(with_this)-1][0], "dB"
 print "------------------------------"

 plot.clf()
 # it seems logpwrfft exchanges the first and second part of the FFT output, we correct it:
 plot.plot(with_this[len(with_this)/2:]+with_this[:len(with_this)/2]) 
 plot.draw()

def plot_power(data_array):

 os.system('clear')
 print '\n\n'
 print "signal strengh at the center:", data_array[len(data_array)/2][0],"dB"
 
 a1.appendleft(data_array[len(data_array)/2][0])
 datatoplot = a1.pop()
 line.set_ydata(a1)
 plot.draw()
 plot.pause(0.00001)


plot.ion()
tb=top_block.top_block()
tb.start()

#Asanka: experiment
a1 = deque([0]*100)
ax = plot.axes(xlim=(0, 100), ylim=(-100, 0))
ax.grid(True)
line, = plot.plot(a1)
plot.ylim([-150,0])
plot.show()

while True:
 fft=tb.msgq_out.delete_head().to_string() # this indeed blocks
 floats=[] 
 for i in range(0,len(fft),4):
  floats.append(struct.unpack_from('f',fft[i:i+4]))
 print "got",len(floats), "floats; FFT size is", tb.fft_size
 i=0
 while i<len(floats): # gnuradio might sometimes send multiple vectors at once
  pack=floats[i:i+tb.fft_size-1]
  #plot_fft(pack)
  plot_power(pack)
  i+=tb.fft_size
  

This python script and the gnuradio-companion generated python script should be there in the same directory. Finally we need one more python script before we can try running this code. It can be obtained from here and saved into the same directory. It's written by Andras Retzler and I got the code from an example he provided in github.

Now I think we are good to go. Connect HackRF device into a USB port of the computer and run our python script with root privileges.

sudo python fft_plotter.py

Depending on the function we call within our script (we cannot call both at the same time), we will be able to see two different plots as follows.




That's it for the moment!

References: