跳至主要內容

GRC 產生的 Python 檔

Jia-Yin大约 4 分鐘comm

一個簡單的 GNU Radio 系統架構

由 GRC 產生出來的 Python 檔

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

#
# SPDX-License-Identifier: GPL-3.0
#
# GNU Radio Python Flow Graph
# Title: Not titled yet
# Author: jyw
# GNU Radio version: 3.10.1.1

from packaging.version import Version as StrictVersion

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()")

from PyQt5 import Qt
from gnuradio import qtgui
from gnuradio.filter import firdes
import sip
from gnuradio import analog
from gnuradio import blocks
from gnuradio import gr
from gnuradio.fft import window
import sys
import signal
from argparse import ArgumentParser
from gnuradio.eng_arg import eng_float, intx
from gnuradio import eng_notation



from gnuradio import qtgui

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

    def __init__(self):
        gr.top_block.__init__(self, "Not titled yet", catch_exceptions=True)
        Qt.QWidget.__init__(self)
        self.setWindowTitle("Not titled yet")
        qtgui.util.check_set_qss()
        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", "basic")

        try:
            if StrictVersion(Qt.qVersion()) < StrictVersion("5.0.0"):
                self.restoreGeometry(self.settings.value("geometry").toByteArray())
            else:
                self.restoreGeometry(self.settings.value("geometry"))
        except:
            pass

        ##################################################
        # Variables
        ##################################################
        self.samp_rate = samp_rate = 32000

        ##################################################
        # Blocks
        ##################################################
        self.qtgui_freq_sink_x_0 = qtgui.freq_sink_c(
            1024, #size
            window.WIN_BLACKMAN_hARRIS, #wintype
            0, #fc
            samp_rate, #bw
            "", #name
            1,
            None # parent
        )
        self.qtgui_freq_sink_x_0.set_update_time(0.10)
        self.qtgui_freq_sink_x_0.set_y_axis(-140, 10)
        self.qtgui_freq_sink_x_0.set_y_label('Relative Gain', 'dB')
        self.qtgui_freq_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, 0.0, 0, "")
        self.qtgui_freq_sink_x_0.enable_autoscale(False)
        self.qtgui_freq_sink_x_0.enable_grid(False)
        self.qtgui_freq_sink_x_0.set_fft_average(1.0)
        self.qtgui_freq_sink_x_0.enable_axis_labels(True)
        self.qtgui_freq_sink_x_0.enable_control_panel(False)
        self.qtgui_freq_sink_x_0.set_fft_window_normalized(False)



        labels = ['', '', '', '', '',
            '', '', '', '', '']
        widths = [1, 1, 1, 1, 1,
            1, 1, 1, 1, 1]
        colors = ["blue", "red", "green", "black", "cyan",
            "magenta", "yellow", "dark red", "dark green", "dark blue"]
        alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
            1.0, 1.0, 1.0, 1.0, 1.0]

        for i in range(1):
            if len(labels[i]) == 0:
                self.qtgui_freq_sink_x_0.set_line_label(i, "Data {0}".format(i))
            else:
                self.qtgui_freq_sink_x_0.set_line_label(i, labels[i])
            self.qtgui_freq_sink_x_0.set_line_width(i, widths[i])
            self.qtgui_freq_sink_x_0.set_line_color(i, colors[i])
            self.qtgui_freq_sink_x_0.set_line_alpha(i, alphas[i])

        self._qtgui_freq_sink_x_0_win = sip.wrapinstance(self.qtgui_freq_sink_x_0.qwidget(), Qt.QWidget)
        self.top_layout.addWidget(self._qtgui_freq_sink_x_0_win)
        self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True)
        self.analog_sig_source_x_0 = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, 1000, 1, 0, 0)


        ##################################################
        # Connections
        ##################################################
        self.connect((self.analog_sig_source_x_0, 0), (self.blocks_throttle_0, 0))
        self.connect((self.blocks_throttle_0, 0), (self.qtgui_freq_sink_x_0, 0))


    def closeEvent(self, event):
        self.settings = Qt.QSettings("GNU Radio", "basic")
        self.settings.setValue("geometry", self.saveGeometry())
        self.stop()
        self.wait()

        event.accept()

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate)
        self.blocks_throttle_0.set_sample_rate(self.samp_rate)
        self.qtgui_freq_sink_x_0.set_frequency_range(0, self.samp_rate)




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

    if StrictVersion("4.5.0") <= StrictVersion(Qt.qVersion()) < StrictVersion("5.0.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 sig_handler(sig=None, frame=None):
        tb.stop()
        tb.wait()

        Qt.QApplication.quit()

    signal.signal(signal.SIGINT, sig_handler)
    signal.signal(signal.SIGTERM, sig_handler)

    timer = Qt.QTimer()
    timer.start(500)
    timer.timeout.connect(lambda: None)

    qapp.exec_()

if __name__ == '__main__':
    main()

程式架構

上面的程式是使用 GNU Radio 和 PyQt5 所建構出來的 Python 應用程式,它主要用來創建一個簡單的系統,能夠生成弦波訊號並分析其訊號頻率。下表是主要區塊和相關的說明:

區塊說明
引入依賴套件引入需要的套件,如PyQt5用於GUI,gnuradio用於信號處理
環境設置檢查和設置運行環境,檢查是否需要載入 X11 環境的程式庫;設定系統訊號的處理方法
類別定義定義basic類別,這是系統的主類別,整合了GUI界面與訊號處理流程
訊號處理模組包括訊號源、節流塊和頻率顯示器
訊號連接連接訊號源、節流塊和頻率顯示器以形成完整的訊號處理流程
GUI 設定設置窗口屬性、佈局和訊號處理模組的參數
主函數初始化基礎類別實例,啓動訊號處理和 GUI 界面
執行程式執行入口,確保程式以主模式運行

詳細説明

  1. 引入依賴套件:文件開始時引入了多個套件,例如PyQt5用於GUI,gnuradio相關模組用於訊號處理。

  2. 環境設置

    • 檢查運行平台是否為Linux,如果是的話則調用libX11.so程式庫,確保 X11 的圖形界面可以正確運行。
    • 設置系統訊號的處理,當接收到中斷或結束訊號時關閉程式。
  3. 類別定義

    • 定義一個名為basic的類別,繼承自gr.top_blockQt.QWidget。這個類別用來設置應用程式的 GUI 界面和訊號處理流程。
    • 在類別的初始化方法中,設置視窗屬性、佈局以及訊號處理模組,包括訊號源analog_sig_source_x_0、節流塊blocks_throttle_0,以及頻率顯示器qtgui_freq_sink_x_0
  4. 訊號處理模組

    • analog_sig_source_x_0: 生成固定頻率的餘弦波訊號源。
    • blocks_throttle_0: 用於控制訊號流的速率,防止過快消耗CPU資源。
    • qtgui_freq_sink_x_0: 用於顯示訊號的頻率分佈。
  5. 訊號連接

    • 將訊號源連接到節流塊,然後輸出到頻率顯示器,以顯示訊號源所發出的訊號頻率分佈。
  6. GUI 設定

    • 設置訊號源的參數,例如取樣速度和頻率。
    • 管理應用視窗的佈置和用户界面設置。
  7. 主函數

    • 檢查Qt版本,設置圖形系統。
    • 創建basic類別的實例,設定系統訊號處理,顯示應用程式。
    • 處理系統訊號和定時器事件,以正確關閉和啓動程式。
  8. 執行:

    • 程式底部的執行入口用來確保當程式作為主程式運行時能執行 main 函數。

練習 1

試著在 GNU Radio 的 Python 環境中執行產生的 Python 程式檔,應該會得到與 GRC 中相同的運行結果。