原标题:如何搭配USRP在安卓设备上搭建GNU Radio

在这篇文章中,我们会使用安卓设备通过USB连接USRP。所以就需要USRP B2xx(B200, B210, 或者是 B200mini),这些都是经过全面测试的。除了这些,我们还需要一个OTG(USB On-the-Go)线缆,它可以插入安卓设备的USB端口。准备好这些,就可以按照下图所示接线方法连接。

点击这里查看已经被测试证明可以正常工作的设备列表。

具有网络连接的USRP似乎更容易在使用安卓设备的情况下正常工作。B2xx系列的USRP最大的问题在于每次开机时都必须对其固件和FPGA图像进行编程,而N200/N210这样的的USRP是预编程的,所以不需要面临某些USB设备的权限问题,联网的USRP所需的权限只有“android.permission.INTERNET”,该权限用于访问ControlPort。

RTL-SDR也可以用于安卓设备上的GNU Radio,与USRP B2xx差不多,这些设备也需要USB权限的替代办法,不过不需要编程。我们可以将RTL-SDR库和gr-osmosdr对其的支持构建到Android依赖包的GR中。此外,增加对其他基于USB的设备(如BladeRF,HackRF,Airspy等)的支持也将是比较直接的。

点击这里查看Github上完整的GrTemplateUSRP示例。

开始

我们现在要构建一个新的应用程序,首先,按照GRAndWalkthroughCP概述中的步骤创建一个新的应用程序项目,不过这次暂且命名为GrTemplateUSRP,这为我们设置了一个支持ControlPort且有一个基本流图的项目。在进行下一步之前,确保应用程序的构建和运行就像GrTemplateCP一样。

其次,克隆并构建GrHardwareService应用程序,并将此应用安装到你的安卓设备上,如果你第一次启动时没有连接USRP,应用可能会崩溃。加载完成后,它会为B2xx设备安装USRP固件和FPGA图像,并开始侦听USB连接。如果插入了供应商和生产ID与任何已知的USRP匹配的设备,则开始对设备进行编程。它会要求你授权两次以使用该设备,一次对固件进行编程,另一次是对FPGA图像进行编程。一旦完成,硬件即准备就绪。

如果你在第一次插入USRP时导致了程序崩溃,则可能是权限问题。在Marshmallow(安卓6.0)上,需要进入设置中的应用程序部分,找到GrHardwareService,然后启用存储权限。这是安卓6.0中的新“功能”。

添加对USB设备的支持

我们必须与安卓设备交互才可以在Java应用程序中获取信息并将其传递给C++流图,值得一提的是,安卓并不会将USB权限交给本地应用程序,所以我们需要在Java中获取文件描述符(fd)和USB文件系统路径(usrpfs_path),并将其通过GNU Radio传递给libUHD,最终通过libUSB访问。

接下来,我们需要添加一些支持以获得USB许可,并找到我们需要连接到USRP的fd和usbfs_path信息。

首先,将这些变量添加到MainActivity类中:

public static Intent intent = null; private String usbfs_path = null; private int fd = -1; private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; private static final String DEFAULT_USBFS_PATH = "/dev/bus/usb"; private UsbManager mUsbManager; private UsbDevice mUsbDevice;

在我们收发无线电之前,我们需要确保已经拥有USB权限。因此,和我们之前一直在OnCreate中做事情不同,我们会将其分成不同部分,首先在OnCreate中,我们需要获取USB设备,设置无线电接收器,然后询问用户是否允许使用该设备。当用户同意时,无线电接收器启动。 即完成对USB设备的访问,现在我们可以对USB设备进行操作,以便我们从中获取需要的信息。这就是OnCreate函数:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create the seekBar to move update the amplitude SeekBar ampSeekBar = (SeekBar) findViewById(R.id.seekBar); ampSeekBar.setMax(100); // max value -> 1.0 ampSeekBar.setProgress(50); // match 0.5 starting value ampSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { Double amp = progress / 100.0; // rescale by 100 RPCConnection.KnobInfo _k = new RPCConnection.KnobInfo(mult_knob_name, amp, BaseTypes.DOUBLE); HashMap _map = new HashMap<>(); _map.put(mult_knob_name, _k); postSetKnobMessage(_map); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); // Start setting up for USB permission request intent = getIntent(); mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(mUsbReceiver, filter); mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (mUsbDevice == null) { Log.d("GrTemplateUSRP", "Didn't get a device; finding it now."); final HashSet allowed_devices = getAllowedDevices(this); final HashMap usb_device_list = mUsbManager.getDeviceList(); for (UsbDevice candidate : usb_device_list.values()) { String candstr = "v" + candidate.getVendorId() + "p" + candidate.getProductId(); if (allowed_devices.contains(candstr)) { // Need to handle case where we have more than one device connected mUsbDevice = candidate; } } } Log.d("GrTemplateUSRP", "Selected Device: " + mUsbDevice); PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); // Launch dialog to ask for permission. // If use hits OK, the broadcast receiver will be launched. mUsbManager.requestPermission(mUsbDevice, permissionIntent); }

需要注意的是,我们仍要在此处创建Seek Bar作为GUI设置。

我们还调用了一个名为getAllowedDevices的新函数。 该函数能够解析目标通信设备的XML文件。因此,如果存在于此文件中列出的设备(基于供应商和产品ID),无线电接收器才会被触发。

现在需要创建device_filter.xml文件。 在res目录中,添加一个名为xml的新目录。在xml目录中,创建一个名为device_filter.xml的新文件。 这个文件如下所示:

将来可能需要在此添加更多设备以用于其他硬件支持。

getAllowedDevices函数如下所示:

private static HashSet getAllowedDevices(final Context ctx) { final HashSet ans = new HashSet(); try { final XmlResourceParser xml = ctx.getResources().getXml(R.xml.device_filter); xml.next(); int eventType; while ((eventType = xml.getEventType()) != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: if (xml.getName().equals("usb-device")) { final AttributeSet as = Xml.asAttributeSet(xml); final Integer vendorId = Integer.valueOf(as.getAttributeValue(null, "vendor-id"), 10); final Integer productId = Integer.valueOf(as.getAttributeValue(null, "product-id"), 10); ans.add("v" + vendorId + "p" + productId); } break; } xml.next(); } } catch (Exception e) { e.printStackTrace(); } return ans; }

我们所注册的无线电接收器mUsbReceiver(点此查看更多信息),如下所示:

private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if(device != null){ mUsbDevice = device; SetupUSB(); } } else { Log.d("GrTemplateUSRP", "Permission denied for device " + device); } } } } };

如果设备注册并且无错误产生,即将其分配给我们的类对象mUsbDevice,并通过调用SetupUSB开始启动应用程序的下一个阶段。

private void SetupUSB() { final UsbDeviceConnection connection = mUsbManager.openDevice(mUsbDevice); if (connection != null) { fd = connection.getFileDeor(); } else { Log.d("GrTemplateUSRP", "Didn't get a USB Device Connection"); finish(); } if (mUsbDevice != null) { usbfs_path = properDeviceName(mUsbDevice.getDeviceName()); } else { Log.d("GrTemplateUSRP", "Didn't get a USB Device"); finish(); } int vid = mUsbDevice.getVendorId(); int pid = mUsbDevice.getProductId(); Log.d("GrTemplateUSRP", "Found fd: " + fd + " usbfs_path: " + usbfs_path); Log.d("GrTemplateUSRP", "Found vid: " + vid + " pid: " + pid); StartRadio(); }

该函数从USB设备获取信息,将其记录到logcat,然后调用StartRadio。我们使用称为properDeviceName的解析函数从设备本身提取USB设备路径:

public final static String properDeviceName(String deviceName) { if (deviceName == null) return DEFAULT_USBFS_PATH; deviceName = deviceName.trim(); if (deviceName.isEmpty()) return DEFAULT_USBFS_PATH; final String[] paths = deviceName.split("/"); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < paths.length - 2; i++) if (i == 0) sb.append(paths[i]); else sb.append("/").append(paths[i]); final String stripped_name = sb.toString().trim(); if (stripped_name.isEmpty()) return DEFAULT_USBFS_PATH; else return stripped_name; }

路径似乎每次都是”/dev/bus/usb”,但这个步骤是必要的,为的是防止某些特殊情况。

最后,我们调用StartRadio,这实际上是我们之前完成的其他的OnCreate工作,但现在我们正在等待USB设备被正确分配。

当我们在装有USRP的设备上构建并运行这个程序时,会弹出一个对话框,询问我们是否要授予USB设备权限。单击确定,但不要单击复选框。因为我们不希望这个应用程序默认与USRP关联,我们希望这是由GrHardwareService应用程序完成的。点击确定后,我们应该看到logcat中的日志消息,它提供了fd,usbfs_path以及发现设备的vid和pid。这确保我们正确地与Java中的USRP交互。然后,我们会将这些信息传递给流图,以创建UHD USRP block。你应该在logcat中看到类似这样的内容:

04-22 11:30:01.624 14537 14537 D GrTemplateUSRP: Didn't get a device; finding it now. 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: Selected Device: UsbDevice[mName=/dev/bus/usb/002/003,mVendorId=9472,mProductId=34,mClass=255,mSubclass=0,mProtocol=0,mManufacturerName=Ettus Research LLC,mProductName=USRP B200,mVersion=2.16,mSerialNumber=30BFC51,mConfigurations=[ 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbConfiguration[mId=1,mName=null,mAttributes=128,mMaxPower=1,mInterfaces=[ 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbInterface[mId=0,mAlternateSetting=0,mName=USRP B200,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[] 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbInterface[mId=1,mAlternateSetting=0,mName=USRP B200,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[ 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=512,mInterval=0]] 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbInterface[mId=2,mAlternateSetting=0,mName=USRP B200,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[ 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbEndpoint[mAddress=134,mAttributes=2,mMaxPacketSize=512,mInterval=0]] 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbInterface[mId=3,mAlternateSetting=0,mName=USRP B200,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[ 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbEndpoint[mAddress=4,mAttributes=2,mMaxPacketSize=512,mInterval=0]] 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbInterface[mId=4,mAlternateSetting=0,mName=USRP B200,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[ 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: UsbEndpoint[mAddress=136,mAttributes=2,mMaxPacketSize=512,mInterval=0]]]] 04-22 11:30:01.626 14537 14537 D GrTemplateUSRP: Called Request Permission 04-22 11:30:01.627 14537 14537 D GrTemplateUSRP: Found fd: 22 usbfs_path: /dev/bus/usb 04-22 11:30:01.627 14537 14537 D GrTemplateUSRP: Found vid: 9472 pid: 34 创建UHD USRP Source

现在我们有权限与我们需要与USRP传递的所有信息。 所以现在可以改变流图了,我们可以使用UHD USRP Source block作为源代替sig_source_f。

首先,我们需要将fd和usrpfs_path值传递给流图。将fg.cpp中的函数签名更改为:

JNIEXPORT void JNICALL Java_org_gnuradio_grtemplateusrp_MainActivity_FgInit(JNIEnv* env, jobject thiz, int fd, jstring devname)

在声明JNI函数的MainActivity.java中,我们需要添加两个参数:

public native void FgInit(int fd, String usbfs_path);

在SetupRadio中,确保将这些信息作为“FgInit(fd,usbfs_path)”传递。

然后我们提取信息并为usrp_source block格式化设备参数字符串:

#include .... const char *usbfs_path = env->GetStringUTFChars(devname, NULL); std::stringstream args; args << "uhd,fd=" << fd << ",usbfs_path=" << usbfs_path; GR_INFO("fg", boost::str(boost::format("Using UHD args=%1%") % args.str())); uhd::stream_args_t stream_args; stream_args.cpu_format = "fc32"; stream_args.otw_format = "sc16"; .... gr::uhd::usrp_source::sptr src; .... src = gr::uhd::usrp_source::make(args.str(), stream_args); src->set_samp_rate(200e3); src->set_center_freq(101.1e6); src->set_gain(20); // adjust as needed

src现在是一个复杂的源。 所以现在要做的是创建一个简单的流图,如下所示:

usrp_source -> complex_to_real -> multiply_const_ff -> opensl_sink

完整的流图FgInit如下所示:

// Get any GNU Radio headers #include #include #include #include #include // Declare the global virtual machine and top-block objects JavaVM *vm; gr::top_block_sptr tb; extern "C" { JNIEXPORT void JNICALL Java_org_gnuradio_grtemplateusrp_MainActivity_FgInit(JNIEnv* env, jobject thiz, int fd, jstring devname) { GR_INFO("fg", "FgInit Called"); const char *usbfs_path = env->GetStringUTFChars(devname, NULL); std::stringstream args; args << "uhd,fd=" << fd << ",usbfs_path=" << usbfs_path; GR_INFO("fg", boost::str(boost::format("Using UHD args=%1%") % args.str())); uhd::stream_args_t stream_args; stream_args.cpu_format = "fc32"; stream_args.otw_format = "sc16"; float samp_rate = 48e3; // 48 kHz // Declare our GNU Radio blocks gr::uhd::usrp_source::sptr src; gr::blocks::complex_to_real::sptr c2r; gr::blocks::multiply_const_ff::sptr mult; gr::grand::opensl_sink::sptr snk; // Construct the objects for every block in the flowgraph tb = gr::make_top_block("fg"); src = gr::uhd::usrp_source::make(args.str(), stream_args); c2r = gr::blocks::complex_to_real::make(); mult = gr::blocks::multiply_const_ff::make(0.0); snk = gr::grand::opensl_sink::make(int(samp_rate)); src->set_samp_rate(200e3); src->set_center_freq(101.1e6); src->set_gain(20); // adjust as needed // Connect up the flowgraph tb->connect(src, 0, c2r, 0); tb->connect(c2r, 0, mult, 0); tb->connect(mult, 0, snk, 0); } ....

这个流图对USRP没有任何特别的用处,但是应该可以接收静态的了。由于没有设置采样率,频率或增益值的事件,因此传入的与任何真实的东西都没有关系。有关USRP应用更复杂的用法,请查看GrRxFM,它使用USRP接收和解调FM广播信号。且设置滑块来调整频率和增益,以及处理流图中的采样率和比率变化。

*参考来源:gnuradio,FB小编Covfefe编译,转载请注明来自FreeBuf.COM返回搜狐,查看更多

责任编辑:

gnuradio android手机,如何搭配USRP在安卓设备上搭建GNU Radio相关推荐

  1. 如何将 Android 手机投屏在 Ubuntu/LinuxMint/Debian 上

    Scrcpy 介绍 首先,我们来认识一下Scrcpy. Scrcpy是一个开源的命令行工具软件,被设计用于使计算机用户能通过android adb或通过usb数据线控制其Android设备,支持通过鼠 ...

  2. ADB投屏_如何将 Android 手机投屏在 Ubuntu/LinuxMint/Debian 上

    你知道如何将 Android 手机投屏到Linux系统吗?本文就以 Scrcpy 软件为例,来讲解一下如何将Android手机投屏到Ubuntu系统. Scrcpy 介绍 首先,我们来认识一下Scrc ...

  3. 在 Android 设备上搭建 Web 服务器

    在 Android 设备上搭建 Web 服务器 叶志陈已关注 12018.04.11 00:20:05字数 1,018阅读 10,291 一般而言,Android 应用在请求数据时都是以 Get 或 ...

  4. react native新建项目运行在安卓设备上

    最近编辑于2018年6月29日 一. 根据http://facebook.github.io/react-native/docs/getting-started.html或者https://react ...

  5. Android 系统的安全性分析(1)--移动设备上的安全威胁

    声明 最近工作上涉及到对Android系统安全性的改造,在改造之前先分析整理下目前Android系统自身的安全性: 参考了一些文章及书籍,在这里大部分是对别人描述的提炼,我挑出一些对我有用的内容整理: ...

  6. 【用PS3手柄在安卓设备上玩游戏系列】连接手柄和设备

    背景 硬件要求1:PS3 手柄 + 手柄配套的USB线 硬件要求2:已经获得 ROOT 权限并且支持蓝牙的安卓设备 软件要求1:Sixaxis Compatibility Checker PS3 手柄 ...

  7. 蓝牙键盘 android手机游戏,使用BluetoothHidDevice将安卓手机同时模拟成鼠标和键盘...

    一直以来就有一种想法,就是自己写一个APP将安卓手机模拟成鼠标/键盘,应急的时候可以用来代替鼠标/键盘.之前也在国内外的网站上找了各种方案,但是这些方案不是很好,直到谷歌发布的API28后终于有了很好 ...

  8. 赤兔android手机视频恢复软件,赤兔安卓手机视频恢复软件

    下面我们对赤兔安卓手机视频恢复软件v10.5官方版文件阐述相关使用资料和赤兔安卓手机视频恢复软件v10.5官方版文件的更新信息. 赤兔安卓手机视频恢复软件 赤兔安卓手机视频恢复软件是一款专门针对安卓系 ...

  9. android手机刷机的作用,安卓手机几种刷机模式作用及分区介绍

    做一个记录,自己用也好,免得下次要刷机又望了,到处找资料. 1.普通启动模式 方法:长按电源键 用途:正常使用 2.fastboot模式 方法一:长按 电源键+音量下键 方法二:抠掉电池,然后长按音量 ...

最新文章

  1. java在mac下开发环境_Java开发环境安装(MacOS、Windows)
  2. LeetCode 475. Heaters
  3. slatstack Master的配置
  4. android NullPointerException (转)
  5. 洛谷P2534 [AHOI2012]铁盘整理
  6. VC菜菜鸟-创建一个简单的多线程任务
  7. Ten graph questions of about 2000 difficulty of Codeforces Round 1
  8. 关于输入阻抗和输出阻抗的理解
  9. 幻幕广告上线,沉浸式体验塑造大片即视感
  10. Roshan.exe 运行直接崩溃
  11. 天津理工大学计算机项目管理实验四,天津理工大学软件工程实验报告4.docx
  12. 服务器硬盘rad技术,服务器硬盘RAD选用.doc
  13. 如何修改PDF,怎么把一个PDF拆分成多个
  14. 2020年开春最新面试!今日头条 Android 面试题及答案 (已拿到 offer)
  15. hikari配置断开重连_Spring boot 数据库连接断线重连问题
  16. 状态压缩Dp模板-玉米田
  17. Web字体格式介绍及浏览器兼容性一览
  18. Master—Theorem 主定理的证明和使用
  19. 千亿级金融场景下,基于Pulsar的云原生消息队列有怎样的表现?
  20. 过来人经验!工作五年以上的UI设计师都在干什么?

热门文章

  1. CTF--misc 零宽度字符隐写
  2. 攻防世界(Pwn) PWN100
  3. python元类的简单了解
  4. 哈尔滨工业大学计算机学院官网,哈尔滨工业大学计算机学院 唐好选 tanghx@hope.hit...
  5. 为什么 scanf( )(scanf_s)函数老要输入两次才行?
  6. 【中级软考】两个人同一日就同样的发明创造申请专利,应该授予谁?
  7. Intel Realsense D435 Post-processing filters 后处理过滤器(用于消除图像的黑洞)
  8. 第九届蓝桥杯大赛软件赛省赛 C/C++ 大学B组
  9. Make Them Equal 埃氏筛法(1200)
  10. RocketMQ源码:NameSrv启动全过程详解