ubuntu 16.04使用LimeSDR 1.4s估算信号到达角DOA

安装依赖的软件包

#安装预编译版本,为后续编译减少依赖,预编译版本太老了
$ sudo apt-get install libarmadillo-dev

$ cd ~

#编译安装最新版本的armadillo,gr-doa需要这个库的支持
$ git clone https://github.com/conradsnicta/armadillo-code.git

$ cd armadillo-code

$ mkdir build 

$ cd build 

$ cmake .. 

$ make

$ sudo make install


#编译安装SoapySDR,为我们后续操作LimeSDR准备操作接口
$ cd ~

$ git clone https://github.com/pothosware/SoapySDR.git

$ cd SoapySDR

$ git pull origin master

$ mkdir build && cd build

$ cmake ..

$ make -j4

$ sudo make install

$ sudo ldconfig


#编译安装GrOsmoSDR,后续为了支持GnuRadio,我们需要GrOsmoSDR帮我们完成一个中转
$ cd ~

$ git clone git://git.osmocom.org/gr-osmosdr

$ cd gr-osmosdr

#修正几处问题
$ sed -i '$a\from _osmosdr_swig import source_IQBalanceOff' ./python/__init__.py

$ sed -i '$a\from _osmosdr_swig import source_IQBalanceManual' ./python/__init__.py

$ sed -i '$a\from _osmosdr_swig import source_IQBalanceAutomatic' ./python/__init__.py

$ mkdir build

$ cd build

$ cmake ..

$ make

$ sudo make install

$ sudo ldconfig

#编译安装LimeSDR的驱动
$ git clone https://github.com/myriadrf/LimeSuite.git

$ cd LimeSuite

#建议2018年4月以后的的主分支,早期版本存在相位随时间漂移的问题
$ git checkout master

# 不可删除build目录,清理build目录后要还原被误删除的文件,
# 原因在于build目录下存在mcu程序,默认应用启动后从这个目录提取mcu程序刷新到设备

$ mkdir build ; cd build

# cmake -DCMAKE_BUILD_TYPE=Debug ..
$ cmake ..

$ make -j4

$ sudo make install

$ sudo ldconfig

$ cd ../udev-rules/

$ sudo ./install.sh

# Download board firmware
$ sudo LimeUtil --update

下载最新的gr-doa源代码

#编译安装gr-doa 
$ cd ~ 

$ git clone https://github.com/EttusResearch/gr-doa.git

增加limesdr的调用代码:

$ sed -i "s/__init__.py/__init__.py\nlimesdr_soapysdr_source.py/g" ~/gr-doa/python/CMakeLists.txt

$ sed -i "s/from twinrx_usrp_source import twinrx_usrp_source/from twinrx_usrp_source import twinrx_usrp_source\nfrom limesdr_soapysdr_source import limesdr_soapysdr_source/g" ~/gr-doa/python/__init__.py

$ vim ~/gr-doa/python/limesdr_soapysdr_source.py

具体代码如下:

# -*- coding: utf-8 -*-
#
# Copyright 2018
# longsky <wangqiang1588@sina.com>
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#

from gnuradio import gr
from gnuradio import blocks
import SoapySDR
from SoapySDR import * #SOAPY_SDR_ constants

import osmosdr
from osmosdr import *

from gnuradio.filter import firdes

def gen_sig_io(num_elements):
    # Dynamically create types for signature
    io = []
    for i in range(num_elements):
        io.append(gr.sizeof_gr_complex*1)
    io.append(gr.sizeof_float*num_elements)
    return io

class limesdr_soapysdr_source(gr.hier_block2):

    def __init__(self, samp_rate=1000000, center_freq=2400000000, gain=40, sources=2):
        gr.hier_block2.__init__(
            self, "LimeSDR SoapySDR",
            gr.io_signature(0, 0, 0),
            gr.io_signaturev(sources, sources, gen_sig_io(sources)),
        )

        ##################################################
        # Parameters
        ##################################################
        self.samp_rate = samp_rate
        self.center_freq = center_freq
        self.gain = gain
        self.sources = sources

        args = dict(driver="lime")
        limesdr = SoapySDR.Device(args)
        # We need to detect the number of channels as the LimeSDR has two
        # and the LimeSDR-Mini has one
        chans = range(limesdr.getNumChannels(0))
        if len(chans) < sources :
            raise Exception("limesdr device doesn't provide enough rx channels ")

        while len(chans) > sources :
            chans.pop()

        limesdr = None
      
        #warnning: args must be written like below 
        args = 'soapy=0,driver=lime,nchan=' + str(sources)
        self.osmosdr_source_0 = osmosdr.source(args)
        self.osmosdr_source_0.set_clock_source('internal', 0)
        self.osmosdr_source_0.set_sample_rate(self.samp_rate)
        #self.osmosdr_source_0.set_time_now(osmosdr.time_spec_t())
        
        for ch in chans:
            self.osmosdr_source_0.set_gain(self.gain, ch)
            # print(self.limesdr.listAntennas(SOAPY_SDR_RX, 0))
            # ('NONE', 'LNAH', 'LNAL', 'LNAW', 'LB1', 'LB2')
            #RX_L : is lower then 2.5Ghz,RX_H : higher than 2.5GhZ
            self.osmosdr_source_0.set_antenna("LNAH", ch)
            #self.osmosdr_source_0.set_bandwidth(ch,20*1000*1000)
            self.osmosdr_source_0.set_dc_offset_mode(osmosdr.source_IQBalanceAutomatic,ch)

        # Use timed commands to set frequencies
     	self.set_center_freq(center_freq,sources)
     	
        ##################################################
        # Connections
        ##################################################
        for source in range(sources):
            self.connect((self.osmosdr_source_0, source), (self, source))


    def __del__(self):
        self.disconnect_all()
        if hasattr(gr.hier_block2,"__del__") :
            gr.hier_block2.__del__(self)

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.osmosdr_source_0.set_sample_rate(self.samp_rate)

    def set_center_freq(self, center_freq, sources):
        self.center_freq = center_freq
        for ch in range(self.sources):
            self.osmosdr_source_0.set_center_freq(self.center_freq, ch)

    def get_center_freq(self):
        return self.center_freq


    def get_gain(self):
        return self.gain

    def set_gain(self, gain):
        print "DO NOT TUNE GAINS DURING RUNTIME"

    def get_sources(self):
        return self.sources

    def set_sources(self, sources):
        print "DO NOT CHANGE SOURCES DURING RUNTIME"

编译代码

$ cd ~

$ cd gr-doa 

$ mkdir build 

$ cd build 

$ cmake .. 

$ make 

$ sudo make install 

$ sudo ldconfig

测试代码

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
##################################################
# GNU Radio Python Flow Graph
# Title: Run Rootmusic Lin Array LimeSDR
# Generated: Tue Apr  3 20:14:45 2018
##################################################

if __name__ == '__main__':
    import ctypes
    import sys
    if sys.platform.startswith('linux'):
        try:
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
            x11.XInitThreads()
        except:
            print "Warning: failed to XInitThreads()"

def struct(data): return type('Struct', (object,), data)()
from PyQt4 import Qt
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filter import firdes
from optparse import OptionParser
import doa
import sys


class run_RootMUSIC_lin_array_LimeSDR(gr.top_block, Qt.QWidget):

    def __init__(self):
        gr.top_block.__init__(self, "Run Rootmusic Lin Array LimeSDR")
        Qt.QWidget.__init__(self)
        self.setWindowTitle("Run Rootmusic Lin Array LimeSDR")
        try:
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
        except:
            pass
        self.top_scroll_layout = Qt.QVBoxLayout()
        self.setLayout(self.top_scroll_layout)
        self.top_scroll = Qt.QScrollArea()
        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
        self.top_scroll_layout.addWidget(self.top_scroll)
        self.top_scroll.setWidgetResizable(True)
        self.top_widget = Qt.QWidget()
        self.top_scroll.setWidget(self.top_widget)
        self.top_layout = Qt.QVBoxLayout(self.top_widget)
        self.top_grid_layout = Qt.QGridLayout()
        self.top_layout.addLayout(self.top_grid_layout)

        self.settings = Qt.QSettings("GNU Radio", "run_RootMUSIC_lin_array_LimeSDR")
        self.restoreGeometry(self.settings.value("geometry").toByteArray())

        ##################################################
        # Variables
        # NormSpacing 天线之间的实际距离,单位为米,类型为浮点类型
        # NumTargets 发出信号的设备数量
        ##################################################
        self.input_variables = input_variables = struct({"SampleRate": 1000000, "CenterFreq": 2550000000, "Gain": 60, "NumArrayElements": 2, "NormSpacing": 0.5, "SnapshotSize": 2**11, "OverlapSize": 2**9, "NumTargets": 1, })

        ##################################################
        # Blocks
        ##################################################
        self.limesdr = doa.limesdr_soapysdr_source(
            samp_rate=input_variables.SampleRate,
            center_freq=input_variables.CenterFreq,
            gain=input_variables.Gain,
            sources=input_variables.NumArrayElements
        )
        self.tab = Qt.QTabWidget()
        self.tab_widget_0 = Qt.QWidget()
        self.tab_layout_0 = Qt.QBoxLayout(Qt.QBoxLayout.TopToBottom, self.tab_widget_0)
        self.tab_grid_layout_0 = Qt.QGridLayout()
        self.tab_layout_0.addLayout(self.tab_grid_layout_0)
        self.tab.addTab(self.tab_widget_0, "Angle of Arrival")
        self.top_layout.addWidget(self.tab)
        self.doa_rootMUSIC_linear_array_0 = doa.rootMUSIC_linear_array(input_variables.NormSpacing, input_variables.NumTargets, input_variables.NumArrayElements)
        self.doa_qt_compass_0 = doa.compass("", 0, 180, 10, 0)
        self.top_layout.addLayout(self.doa_qt_compass_0.this_layout)
        self.doa_autocorrelate_0 = doa.autocorrelate(input_variables.NumArrayElements, input_variables.SnapshotSize, input_variables.OverlapSize, 1)

        ##################################################
        # Connections
        ##################################################
        self.connect((self.doa_autocorrelate_0, 0), (self.doa_rootMUSIC_linear_array_0, 0))    
        self.connect((self.doa_rootMUSIC_linear_array_0, 0), (self.doa_qt_compass_0, 0))
        for ch in range(input_variables.NumArrayElements) :
            self.connect((self.limesdr, ch), (self.doa_autocorrelate_0, ch))    

    def closeEvent(self, event):
        self.settings = Qt.QSettings("GNU Radio", "run_RootMUSIC_lin_array_LimeSDR")
        self.settings.setValue("geometry", self.saveGeometry())
        event.accept()

def main(top_block_cls=run_RootMUSIC_lin_array_LimeSDR, options=None):

    from distutils.version import StrictVersion
    if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"):
        style = gr.prefs().get_string('qtgui', 'style', 'raster')
        Qt.QApplication.setGraphicsSystem(style)
    qapp = Qt.QApplication(sys.argv)

    tb = top_block_cls()
    tb.start()
    tb.show()

    def quitting():
        tb.stop()
        tb.wait()
    qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting)
    qapp.exec_()


if __name__ == '__main__':
    main()

如果上面的代码下载困难,可从本站下载一份代码拷贝

gr-doaarmadillo-codeSoapySDR源代码下载gr-osmosdr

至于测试的信号发送设备,任何能产生自相关信号的设备都可以,一般建议对讲机即可,注意频率要调整到一致才可以。

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注