[SDR] GNU Radio 系列教程(十四) —— GNU Radio 低阶到高阶用法的分水岭 ZMQ 的使用详解

科技资讯 投稿 5400 0 评论

[SDR] GNU Radio 系列教程(十四) —— GNU Radio 低阶到高阶用法的分水岭 ZMQ 的使用详解


目录
    1、前言
  • 2、ZMQ 块的类型
  • 3、ZMQ 块的使用
  • 4、DEMO
    • 4.1 同一台电脑上的两个流程图
    • 4.2 不同电脑上的两个流程图
    • 4.3 作为 REQ/REP 服务器的 Python 程序
    • 4.4 作为 PUSH/PULL 服务器的 Python 程序
    • 4.5 处理流程图数据的 Python 程序
  • 参考链接

1、前言

因为学会了 ZMQ,就可以将 GNU Radio 中的实时数据流通过 socket 引到外面的 python/c 等大型应用程序中,做数据分析、展示、人工智能等。

2、ZMQ 块的类型

SINK SOURCE 特征
PUB SUB 广播,可一对多
PUSH PULL 点播,点对点对等网络
REQ REP 点对点链路,一个请求一个回复,类似客户端服务器

Data Blocks:
ZMQ data blocks 传输原始流数据;没有格式化。数据类型和采样率由馈送 ZMQSink 的流程图确定。因此,接收数据的流程图或程序必须知道这些参数,以便正确地解释数据。


Message Blocks
不像普通的 ZeroMQ 字符串,GNU Radio ZMQ Message Blocks 使用 PMT 对数据进行编码和解码。


3、ZMQ 块的使用

    ZMQ 块的用户应该对ZeroMQ有一些熟悉。特别是,应该认识到ZMQ套接字和BSD套接字之间的区别。有关概述,请参阅 ZMQ Socket API。
  • ZMQ 块使用 endpoints 来描述 ZMQ 应该如何传递数据。常见 endpoints 使用 TCP 传输数据,当然也可以采用其他协议。想要了解不同协议的 endpoints,可以参阅 zmq_tcpzmq_ipc
  • 可以在49152–65535范围内分配专用端口。
  • 不建议在单个流程图中使用ZMQ块,因为 Virtual_SourceVirtual_Sink 块的效率要高得多。

TCP Bind vs Connect

首先要注意,在任何拓扑中,必须有一个到给定端点的绑定,而可能有多个到同一端点的连接。(A-B 之间绑定一次,可能会出现多个连接)

还要注意:TCP端点的语义在绑定和连接之间有所不同。


TCP Bind

如果您指定了一个IP地址,则说明 socket 只接受与该地址相关联的网络上的连接(例如:127.0.0.1 or 192.168.1.123

0.0.0.0 作为通配符地址;尽管 ZMQ 接受 * 作为通配符,但它并不是在所有情况下都能很好地工作。因此,您可以选择绑定到 tcp://0.0.0.0:54321

zmq_tcp


TCP Connect

Wire Format

当过滤器字符串被提供给 PUB/SUB 块时,GNU Radio 使用多部分消息来发送过滤器字符串,然后是有效载荷。尝试与 GNU Radio ZMQ 块接口的非 GNU 无线电代码必须为此部分准备好,并将其丢弃。请注意,只有在指定了非空筛选器的情况下,发送方才会发送此消息部分。

这两个特征使得发送器配置与接收器配置的匹配变得至关重要。否则将导致流程图中出现运行时错误。


4、DEMO

4.1 同一台电脑上的两个流程图

127.0.0.1。它的开销比完整的IP要小。

这个流程图我也在 B 站上面有详细的视频介绍:GNU Radio 系列教程(十二)-- 窄带 FM 收发系统(基于ZMQ模拟射频发送)


4.2 不同电脑上的两个流程图

192.168.1.194:50241,Source 位于 IP 192.168.1.85,则 Source 和 Sink 块都必须指定 Sink IP 和端口 192.168.1.194:00241


4.3 作为 REQ/REP 服务器的 Python 程序

GR ZMQ_REP_Message_Sink,并返回到 ZMQ_REQ_Message_Source

只需记住: sink 是流程图的终点,source 是流程图的起点。

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# zmq_REQ_REP_server.py

# This server program capitalizes received strings and returns them.
# NOTES:
#   1 To comply with the GNU Radio view, messages are received on the REQ socket and sent on the REP socket.
#   2 The REQ and REP messages must be on separate port numbers.

import pmt
import zmq

_debug = 0          # set to zero to turn off diagnostics

# create a REQ socket
_PROTOCOL = "tcp://"
_SERVER = "127.0.0.1"          # localhost
_REQ_PORT = ":50246"
_REQ_ADDR = _PROTOCOL + _SERVER + _REQ_PORT
if (_debug:
    print ("'zmq_REQ_REP_server' version 20056.1 connecting to:", _REQ_ADDR
req_context = zmq.Context(
if (_debug:
    assert (req_context
req_sock = req_context.socket (zmq.REQ
if (_debug:
    assert (req_sock
rc = req_sock.connect (_REQ_ADDR
if (_debug:
    assert (rc == None

# create a REP socket
_PROTOCOL = "tcp://"
_SERVER = "127.0.0.1"          # localhost
_REP_PORT = ":50247"
_REP_ADDR = _PROTOCOL + _SERVER + _REP_PORT
if (_debug:
    print ("'zmq_REQ_REP_server' version 20056.1 binding to:", _REP_ADDR
rep_context = zmq.Context(
if (_debug:
    assert (rep_context
rep_sock = rep_context.socket (zmq.REP
if (_debug:
    assert (rep_sock
rc = rep_sock.bind (_REP_ADDR
if (_debug:
    assert (rc == None

while True:
    #  Wait for next request from client
    data = req_sock.recv(
    message = pmt.to_python(pmt.deserialize_str(data
    print("Received request: %s" % message

    output = message.upper(

    #  Send reply back to client
    rep_sock.send (pmt.serialize_str(pmt.to_pmt(output

安装 NetCat:方便我们测试 TCP
-《NetCat使用指南》
-《Sending TCP/UDP packets using Netcat》
-《Simple client / server with nc not working》
注意,这鬼软件有好几个不同的软件,我用的是 openbsd-netcat

sudo pacman -S openbsd-netcat

上面代码:

kind port method func C/S
REQ 50246 connect recv( server
REP 50247 bind send( client


4.4 作为 PUSH/PULL 服务器的 Python 程序

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# zmq_PUSH_PULL_server.py

import sys
import pmt
import zmq

_debug = 0          # set to zero to turn off diagnostics

# create a PUSH socket
_PROTOCOL = "tcp://"
_SERVER = "127.0.0.1"          # localhost
_PUSH_PORT = ":50252"
_PUSH_ADDR = _PROTOCOL + _SERVER + _PUSH_PORT
if (_debug:
    print ("'zmq_PUSH_PULL_server' version 20068.1 binding to:", _PUSH_ADDR
push_context = zmq.Context(
if (_debug:
    assert (push_context
push_sock = push_context.socket (zmq.PUSH
if (_debug:
    assert (push_sock
rc = push_sock.bind (_PUSH_ADDR
if (_debug:
    assert (rc == None

# create a PULL socket
_PROTOCOL = "tcp://"
_SERVER = "127.0.0.1"          # localhost
_PULL_PORT = ":50251"
_PULL_ADDR = _PROTOCOL + _SERVER + _PULL_PORT
if (_debug:
    print ("'zmq_PUSH_PULL_server' connecting to:", _PULL_ADDR
pull_context = zmq.Context(
if (_debug:
    assert (pull_context
pull_sock = pull_context.socket (zmq.PULL
if (_debug:
    assert (pull_sock
rc = pull_sock.connect (_PULL_ADDR
if (_debug:
    assert (rc == None

while True:
    #  Wait for next request from client
    data = pull_sock.recv(
    message = pmt.to_python(pmt.deserialize_str(data
    # print("Received request: %s" % message

    output = message.upper(    # capitalize message

    #  Send reply back to client
    push_sock.send (pmt.serialize_str(pmt.to_pmt(output

4.5 处理流程图数据的 Python 程序

个 demo 是几乎贯穿后面 GNU Radio 高阶用法的最重要的 DEMO。 因为,通常情况下我们会使用 GNU Radio 进行信号处理,但希望数据流流入普通 python 程序,然后做丰富的数据分析等逻辑。这里,PUB 和 PUSH 可以让应用程序获得这些数据流。(这里我们将 127.0.0.1 换成了 *,这样能够让同一局域网内的设备都能访问

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# zmq_SUB_proc.py
# Author: Marc Lichtman

import zmq
import numpy as np
import time
import matplotlib.pyplot as plt

context = zmq.Context(
socket = context.socket(zmq.SUB
socket.connect("tcp://127.0.0.1:55555" # connect, not bind, the PUB will bind, only 1 can bind
socket.setsockopt(zmq.SUBSCRIBE, b'' # subscribe to topic of all (needed or else it won't work

while True:
    if socket.poll(10 != 0: # check if there is a message on the socket
        msg = socket.recv( # grab the message
        print(len(msg # size of msg
        data = np.frombuffer(msg, dtype=np.complex64, count=-1 # make sure to use correct data type (complex64 or float32; '-1' means read all data in the buffer
        print(data[0:10]
        # plt.plot(np.real(data
        # plt.plot(np.imag(data
        # plt.show(
    else:
        time.sleep(0.1 # wait 100ms and try again

参考链接

[1]. GNU Radio 系列教程(一) —— 什么是 GNU Radio
[2]. GNU Radio 系列教程(二) —— 绘制第一个信号分析流程图
[3]. GNU Radio 系列教程(三) —— 变量的使用
[4]. GNU Radio 系列教程(四) —— 比特的打包与解包
[5]. GNU Radio 系列教程(五) —— 流和向量
[6]. GNU Radio 系列教程(六) —— 基于层创建自己的块
[7]. GNU Radio 系列教程(七)—— 创建第一个块
[8]. GNU Radio 系列教程(八)—— 创建能处理向量的 Python 块
[9]. GNU Radio 系列教程(九)—— Python 块的消息传递
[10]. GNU Radio 系列教程(十)—— Python 块的 Tags
[11]. GNU Radio 系列教程(十一)—— 低通滤波器
[12]. GNU Radio 系列教程(十二)—— 窄带 FM 收发系统(基于ZMQ模拟射频发送)
[13]. GNU Radio 系列教程(十三)—— 用两个 HackRF 实现 FM 收发
[14]. SDR 教程实战 —— 利用 GNU Radio + HackRF 做 FM 收音机
[15]. SDR 教程实战 —— 利用 GNU Radio + HackRF 做蓝牙定频测试工具(超低成本)


如果觉得不错,帮忙点个支持哈~

编程笔记 » [SDR] GNU Radio 系列教程(十四) —— GNU Radio 低阶到高阶用法的分水岭 ZMQ 的使用详解

赞同 (29) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽