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

来源:https://www.cnblogs.com/zjutlitao/archive/2023/04/26/17354483.html
-Advertisement-
Play Games

1、前言 學會使用 GNU Radio 中的 ZMQ,是從低階使用者向高階邁進的第一步! 因為學會了 ZMQ,就可以將 GNU Radio 中的實時數據流通過 socket 引到外面的 python/c 等大型應用程式中,做數據分析、展示、人工智慧等。 來自 ZeroMQ 官方介紹:ZeroMQ ( ...



目錄


1、前言

學會使用 GNU Radio 中的 ZMQ,是從低階使用者向高階邁進的第一步!

因為學會了 ZMQ,就可以將 GNU Radio 中的實時數據流通過 socket 引到外面的 python/c 等大型應用程式中,做數據分析、展示、人工智慧等。

來自 ZeroMQ 官方介紹:ZeroMQ (0MQ, ZMQ),看起來像是一個可嵌入的網路庫,同時起到了併發框架的作用。它為您提供了在進程內、進程間、TCP和多播等各種傳輸中承載原子消息的 socket 。


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

一些用戶可能會想直接連接到GNU Radio ZMQ Blocks。雖然這是可能的,但需要謹慎。

首先要註意,在任何拓撲中,必須有一個到給定端點的綁定,而可能有多個到同一端點的連接。(A-B 之間綁定一次,可能會出現多個連接)

在 GNU Radio 中,stream sinks bind and stream sources connect。Message blocks 取決於參數設置。

還要註意:TCP端點的語義在綁定和連接之間有所不同。


TCP Bind

當綁定一個 TCP 端點時,您可以指定要偵聽的連接點。

如果您指定了一個IP地址,則說明 socket 只接受與該地址相關聯的網路上的連接(例如:127.0.0.1 or 192.168.1.123

在某些情況下,您可能希望在連接到節點的所有網路上進行偵聽。對於 GNU Radio,您應該使用 0.0.0.0 作為通配符地址;儘管 ZMQ 接受 * 作為通配符,但它並不是在所有情況下都能很好地工作。因此,您可以選擇綁定到 tcp://0.0.0.0:54321

請註意,如果您沒有輸入IP地址,bind 會將該值視為網路適配器名稱(例如 eth0)。詳細信息參閱:zmq_tcp


TCP Connect

連接 TCP 端點時,您可以指定要連接的遠程端點。您可以指定 IP 地址或 DNS 可解析名稱。


Wire Format

ZMQ stream blocks 具有傳遞標記的選項。此外,PUB/SUB塊支持過濾。這兩個選項都會影響 ZMQ-wire 協議。

當過濾器字元串被提供給 PUB/SUB 塊時,GNU Radio 使用多部分消息來發送過濾器字元串,然後是有效載荷。嘗試與 GNU Radio ZMQ 塊介面的非 GNU 無線電代碼必須為此部分準備好,並將其丟棄。請註意,只有在指定了非空篩選器的情況下,發送方纔會發送此消息部分。

接下來,如果啟用了發送標簽,則要發送的數據視窗內的任何標簽都將以特殊格式編碼,併在有效載荷數據之前進行預處理。如果未啟用標記,則會忽略此標頭。

這兩個特征使得發送器配置與接收器配置的匹配變得至關重要。否則將導致流程圖中出現運行時錯誤。


4、DEMO

4.1 同一臺電腦上的兩個流程圖

在同一個電腦上時,localhost 的 IP 地址應為 127.0.0.1。它的開銷比完整的IP要小。

下麵使用 PUB/SUB 的流程圖來自 Simulation_example:_AM_transmitter_and_receiver:

這個流程圖我也在 B 站上面有詳細的視頻介紹:GNU Radio 系列教程(十二)-- 窄帶 FM 收發系統(基於ZMQ模擬射頻發送)


4.2 不同電腦上的兩個流程圖

在不同電腦上時,則必須在該連接的每一端指定接收塊(sink block) 的 IP 和埠號。例如,如果 Sink 位於 IP 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 程式

下麵的 Python 程式在其 REQ socket 上接收字元串消息,將文字變成大寫,然後在其 REP socket 上發送出去。術語在這裡變得混亂,因為傳入的 REQ 來自 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

while 迴圈中用 REQ 等待接收,然後轉為大寫,用 REP 發送出去:(比較坑的是,我用 netcat 建立 tcp 伺服器和客戶端,無法與上面 python 腳本通信,似乎一啟動,建立連接,server 就異常退出了,最終還是得用 GNN Radio 開啟兩個 ZMQ 工程,然後與這個 python 腳本通信,整體信息流如下:)


4.4 作為 PUSH/PULL 伺服器的 Python 程式

與上面 demo 類似,是基於 PUSH/PULL 傳遞消息。

#!/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 換成了 *,這樣能夠讓同一區域網內的設備都能訪問)

一般的,流程圖中採用 PUB/PUSH Sink,將數據送出:

然後,普通 python 腳本就可以對其進行 recv:

#!/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 做藍牙定頻測試工具(超低成本)


: 如果覺得不錯,幫忙點個支持哈~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 來源:https://developer.aliyun.com/article/705239 1 什麼是KeyDB? KeyDB是Redis的高性能分支,專註於多線程,記憶體效率和高吞吐量。除了多線程之外,KeyDB還具有僅在Redis Enterprise中可用的功能,例如Active Replic ...
  • Rust + Bevy 實現的 Flappy Bird 游戲 簡介 一個使用 bevy 引擎復刻的 Flappy Bird 經典小游戲。 通過該項目我們可以學到:bevy 的自定義組件,自定義插件,自定義資源,sprite 的旋轉,sprite 的移動,sprite sheet 動畫的定義使用,狀態 ...
  • skywalking是使用位元組碼操作技術和AOP概念攔截Java類方法的方式來追蹤鏈路的,由於skywalking已經打包了位元組碼操作技術和鏈路追蹤的上下文傳播,因此只需定義攔截點即可。 這裡以skywalking-8.7.0版本為例。 關於插件攔截的原理,可以看我的另一篇文章:skywalking ...
  • 在微服務架構中,一次請求可能會被多個服務處理,而每個服務又會產生相應的日誌,且每個服務也會有多個實例。在這種情況下,如果系統發生異常,沒有 Trace ID,那麼在進行日誌分析和追蹤時就會非常困難,因為我們無法將所有相關的日誌信息串聯起來。 如果將 Trace ID 添加到響應頭中,那麼在進行日誌分 ...
  • 前言 在C語言中,賦值運算符用於將一個值賦給變數 這個過程分為兩個步驟: 計算賦值運算符右側的表達式 將結果賦給左側的變數。 C語言提供了多個不同的賦值運算符,包括基本的賦值運算符、複合賦值運算符以及條件賦值運算符等 一、人物簡介 第一位閃亮登場,有請今後會一直教我們C語言的老師 —— 自在。 第二 ...
  • 1.實驗器材 Arduino開發板,麵包板一塊,杜邦線若幹,LED燈若幹,220歐電阻若幹,蜂鳴器一個。 2.實驗操作 將六個led燈的正極依次接到arduino板I/O介面的2-7口, 電源負極依分別接一個220歐的電阻 。 整體負極接arduino板的GND介面。 蜂鳴器正極接arduino板I ...
  • dynamic簡介 dynamic是.NET Framework4.0的新特性。dynamic的出現讓C#具有了弱語言類型的特性。編譯器在編譯的時候不再對類型進行檢查,編譯時預設dynamic對象支持你想要的任何特性。 dynamic簡化反射實現 使用dynamic來簡化反射實現是一種比較常見的編程 ...
  • 隨著技術的發展,ASP.NET Core MVC也推出了好長時間,經過不斷的版本更新迭代,已經越來越完善,本系列文章主要講解ASP.NET Core MVC開發B/S系統過程中所涉及到的相關內容,適用於初學者,在校畢業生,或其他想從事ASP.NET Core MVC 系統開發的人員。 經過前幾篇文章... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...