Skip to main content

Code Explanation

Jia-YinAbout 7 mincomm

This section will provide a detailed explanation for the Python file generated by GRC.

Importing Packages

At the beginning of the file, many packages are imported:

from packaging.version import Version as StrictVersion
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
import ctypes

The table below briefly describes the function of each imported package:

PackageDescription
packaging.versionSoftware version used to check version compatibility.
ctypesMainly used for calling C language libraries.
sysUsed for receiving command line arguments or checking the platform.
PyQt5Python version of the Qt graphical framework.
gnuradioMain package of GNU Radio, including multiple submodules for signal processing and GUI functionality.
sipCreates Python bindings for C/C++ libraries.
signalSystem signal handling used for terminating applications.
argparseParses command line arguments to create a user-friendly command line interface.
gnuradio.eng_argHandles parameter conversions in GNU Radio applications.
gnuradio.eng_notationAssists in handling engineering notation in GNU Radio parameters, such as converting 1e6 to 1M.

Classes and Constructor

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

Here, basic is declared as a class inherited from gr.top_block and Qt.QWidget.

The __init__ function is a special method, commonly known as the class constructor. It is automatically called when a new instance of the class is created, primarily used to initialize the state or attributes of the newly created object.

Inheritance and Multiple Initializations:

  • gr.top_block.__init__(self, "Not titled yet", catch_exceptions=True): This line calls the constructor of gr.top_block, setting the title of the system and enabling exception handling.
  • Qt.QWidget.__init__(self): This line calls the constructor of Qt.QWidget, giving it GUI functionalities such as displaying windows.

Setting Window Attributes:

  • self.setWindowTitle("Not titled yet"): Sets the window title.

Further explanation for the following code:

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)

This code is mainly used to create the layout of the window, including elements such as a scroll area.


Next:

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

This code uses Qt.QSettings to save and restore certain settings of the user interface, such as window position and size. When accessing settings, it checks the Qt version for appropriate handling.

Self

In Python classes, self is a reference to the current object (class instance), used to access instance attributes and class methods. Using self in the __init__ method is to set the initial state of the instance or call other methods to perform operations.

This method of initializing class instances using the __init__ method is a fundamental concept in object-oriented languages, allowing each newly created object to have its own independent attributes and methods. In this case, self ensures that each instance of the basic class can correctly set its graphical interface and internal state, making it fully functional in GNU Radio.

Module (Block) Configuration

##################################################
# 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)

In this code snippet, there are two main parts: variable definitions and block configurations.

Variable Definitions

self.samp_rate = samp_rate = 32000

This line defines the samp_rate variable, with a value set to 32000, representing the sampling rate, i.e., the number of samples processed per second.

Block Configurations

self.qtgui_freq_sink_x_0 = qtgui.freq_sink_c(
    1024, # size
    window.WIN_BLACKMAN_hARRIS, # window type
    0, # center frequency
    samp_rate, # bandwidth
    "", # name
    1,
    None # parent
)
  • qtgui_freq_sink_c is a component in GNU Radio used to create frequency response plots. Here, the following parameters are used:
    • 1024: FFT size, determining frequency resolution.
    • window.WIN_BLACKMAN_hARRIS: Window function type used to smooth the signal for reducing spectral leakage.
    • 0: Center frequency, set to 0 here, usually indicating baseband signal processing.
    • samp_rate: Bandwidth, using the previously defined sampling rate.
    • Empty string or None, indicating this is a standalone component without specific UI label or embedded into another Qt parent component.

Further Configurations

  • set_update_time(0.10): Set update time to 0.10 seconds, refreshing the GUI every 0.10 seconds.
  • set_y_axis(-140, 10): Set the Y-axis range from -140 dB to 10 dB.
  • set_y_label('Relative Gain', 'dB'): Set the Y-axis label as "Relative Gain" with unit dB.
  • set_trigger_mode(qtgui.TRIG_MODE_FREE, 0.0, 0, ""): Set trigger mode to free run, not based on specific signal triggers for updating the plot.
  • enable_autoscale(False): Disable autoscaling to fix the Y-axis range.
  • enable_grid(False): Do not display grid lines for a cleaner GUI.
  • set_fft_average(1.0): Set FFT average to 1.0, meaning no averaging is done, directly showing the result of each computation.
  • enable_axis_labels(True): Enable axis label display.
  • enable_control_panel(False): Do not display the control panel.
  • set_fft_window_normalized(False): Set FFT window to not be normalized.

These settings define how the frequency analysis of signals is displayed in the user interface, ensuring users can adjust and observe signal characteristics as needed.


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)

The purpose of this code snippet is to set and adjust the visual properties of multiple data lines (i.e., frequency responses of signals) in the frequency display, and embed the frequency plot component in the user interface. Here is a further detailed explanation:

Setting Visual Properties of Data Lines

  1. labels, widths, colors, alphas:

    • labels: Defines the labels of the data lines, where all labels are set as empty strings here.
    • widths: Defines the width of each data line, all set to 1.
    • colors: Defines the colors of each data line, including blue, red, green, black, cyan, etc.
    • alphas: Defines the transparency of each data line, all set to 1.0 (fully opaque).
  2. Iteration Setup:

    • for i in range(1): This loop actually iterates only once, setting the properties of the first data line.
    • Within the loop, it checks the length of labels[i], if it is 0 (i.e., no label), it sets the label of the data line to "Data {0}".format(i) (i.e., "Data 0").
    • Then, it sets the width, color, and transparency of that data line.

Embedding Components and Signal Source Configuration

  • self._qtgui_freq_sink_x_0_win = sip.wrapinstance(self.qtgui_freq_sink_x_0.qwidget(), Qt.QWidget): This line of code converts the Qt component of the frequency plot to a PyQt5 component using sip.wrapinstance.

  • self.top_layout.addWidget(self._qtgui_freq_sink_x_0_win): Adds the converted frequency plot component to the top-level layout of the interface.

  • self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True): Creates a throttle block to control the data flow rate to avoid excessive CPU usage.

  • self.analog_sig_source_x_0 = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, 1000, 1, 0, 0): Creates an analog signal source, generating a cosine wave with parameters:

    • samp_rate: Sampling rate of the signal.
    • analog.GR_COS_WAVE: Type of waveform generated, which is a cosine wave here.
    • 1000: Frequency of the signal.
    • 1: Amplitude of the signal.
    • 0, 0: Phase offset and initial phase.

Through these settings, the application configures a graphical interface displaying the frequency response of a cosine wave with a frequency of 1000 Hz, while controlling the data processing rate to adapt to the system's performance requirements.


##################################################
# 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))

This code snippet is mainly responsible for establishing data connections between various processing blocks. In GNU Radio, data flows through connecting different processing blocks (Blocks) to pass signal data.

Connection Process Explanation

  1. Connect Source to Throttle Block:

    self.connect((self.analog_sig_source_x_0, 0), (self.blocks_throttle_0, 0))
    
    • This line of code establishes a connection from the analog signal source (self.analog_sig_source_x_0) to the throttle block (self.blocks_throttle_0).
    • self.analog_sig_source_x_0, 0 represents the output port of the signal source (port number 0).
    • self.blocks_throttle_0, 0 represents the input port of the throttle block (port number 0).
    • This connection ensures that signal data generated by the source can be passed to the throttle block, which is used to control the data flow rate to adapt to CPU usage.
  2. Connect Throttle Block to Frequency Plot Display:

    self.connect((self.blocks_throttle_0, 0), (self.qtgui_freq_sink_x_0, 0))
    
    • This line of code connects the output of the throttle block to the frequency plot display (self.qtgui_freq_sink_x_0).
    • self.blocks_throttle_0, 0 represents the output port of the throttle block (port number 0).
    • self.qtgui_freq_sink_x_0, 0 represents the input port of the frequency plot display (port number 0).
    • Through this connection, the signal data from the throttle block is sent to the frequency plot display for showing the frequency distribution on the graphical user interface.

These connections define the flow of signal data between different processing blocks. With such configurations, signal acquisition, processing, and visualization can be achieved, forming a complete signal processing system, which is one of the reasons why GNU Radio is widely used in software-defined radio and signal processing fields.

User Interface Operations

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)

In this code snippet, several methods are defined to handle window close events, as well as to get and set the sampling rate. These methods are member functions of the basic class, closely related to the signal processing and user interface of the GNU Radio application. Here is a detailed explanation:

closeEvent Method

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

    event.accept()
  • Function: This method handles the close event of the Qt window. It is called when the window is about to close.
  • Settings: Saves the window's geometry layout settings using Qt.QSettings for restoring the same position and size next time the application is opened.
    • "GNU Radio", "basic": Specifies the organization and application names for storing settings.
    • self.saveGeometry(): Saves the current geometry layout of the window.
  • Stop Operations: Calls self.stop() and self.wait() methods to stop and wait for all processing blocks in the system to run correctly, ensuring resources are released properly.
  • Accept Event: Confirms the close event by event.accept(), allowing the window to close.

get_samp_rate Method

def get_samp_rate(self):
    return self.samp_rate
  • Function: This method is used to get the current sampling rate setting.
  • Return: Returns the sampling rate value stored in the self.samp_rate attribute.

set_samp_rate Method

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)
  • Function: This method is used to set a new sampling rate and update the configurations of relevant processing blocks.
  • Update Sampling Rate:
    • self.samp_rate = samp_rate: Updates the sampling rate attribute of the class instance.
    • self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate): Sets the sampling frequency of the signal source block.
    • self.blocks_throttle_0.set_sample_rate(self.samp_rate): Sets the sample rate of the throttle block to control the data flow rate.
    • self.qtgui_freq_sink_x_0.set_frequency_range(0, self.samp_rate): Sets the frequency range of the frequency plot display.

These methods provide users with a flexible interface to conveniently control and adjust key parameters of GNU Radio applications, ensuring resources are handled correctly when the application closes.

Main Program

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