Skip to content

Runtime Communication

This document explains how GNU Radio Companion (GRC) communicates with running flowgraph processes and the two mechanisms available for runtime control.

Key Insight: GRC is a Code Generator

GRC runs flowgraphs as completely separate subprocesses via subprocess.Popen(). It does not have built-in runtime control capabilities.

┌────────────────────┐ subprocess.Popen() ┌─────────────────────┐
│ GNU Radio │ ────────────────────────► │ Generated Python │
│ Companion (GRC) │ │ Flowgraph Script │
│ │ ◄──────────────────────── │ │
│ (Qt/GTK GUI) │ stdout/stderr pipe │ (gr.top_block) │
└────────────────────┘ └─────────────────────┘

The generated Python script runs independently. To control parameters at runtime, you must use one of the two communication mechanisms described below.

GRC Execution Flow

.grc file (YAML)
▼ Platform.load_and_generate_flow_graph()
Generator (Mako templates)
▼ generator.write()
Python script (with set_*/get_* methods)
▼ ExecFlowGraphThread -> subprocess.Popen()
Running flowgraph process
▼ stdout/stderr piped back to GRC console

Key GRC Execution Files

FilePurpose
grc/main.pyEntry point
grc/gui_qt/components/executor.pyExecFlowGraphThread subprocess launcher
grc/core/platform.pyBlock registry, flowgraph loading
grc/core/generator/Generator.pyGenerator factory
grc/workflows/common.pyBase generator classes
grc/workflows/python_nogui/flow_graph_nogui.py.makoMako template for Python

Two Runtime Control Mechanisms

1. XML-RPC Server (Simple, HTTP-based)

A block-based approach — add the xmlrpc_server block to your flowgraph to expose GRC variables over HTTP.

AspectDetails
ProtocolHTTP (XML-RPC)
Default Port8080
SetupAdd XMLRPC Server block to flowgraph
Namingset_varname() / get_varname()
Type SupportBasic Python types

How It Works

  1. Add XMLRPC Server block to flowgraph
  2. GRC variables automatically become set_X() / get_X() methods
  3. Connect with any XML-RPC client (Python, C++, curl, etc.)

Client Example

import xmlrpc.client
# Connect to running flowgraph
server = xmlrpc.client.ServerProxy('http://localhost:8080')
# Read and write variables
print(server.get_freq()) # Read a variable
server.set_freq(145.5e6) # Set a variable
# Flowgraph control
server.stop() # Stop flowgraph
server.start() # Start flowgraph
server.lock() # Lock flowgraph for modifications
server.unlock() # Unlock flowgraph

Key Files

FilePurpose
gr-blocks/grc/xmlrpc_server.block.ymlServer block definition
gr-blocks/grc/xmlrpc_client.block.ymlClient block definition
gr-blocks/examples/xmlrpc/Example flowgraphs

2. ControlPort/Thrift (Advanced, Binary)

A configuration-based approach — blocks register their parameters via setup_rpc() in C++ code.

AspectDetails
ProtocolThrift Binary TCP
Default Port9090
SetupEnable in config, blocks call setup_rpc()
Namingblock_alias::varname
Type SupportRich (complex, vectors, PMT types)
MetadataUnits, min/max, display hints

Architecture

┌──────────────────────────────────────────────────────────────────┐
│ Running Flowgraph Process │
├──────────────────────────────────────────────────────────────────┤
│ Block A Block B │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ setup_rpc() { │ │ setup_rpc() { │ │
│ │ add_rpc_var( │ │ add_rpc_var( │ │
│ │ "gain", │ │ "freq", │ │
│ │ &get_gain, │ │ &get_freq, │ │
│ │ &set_gain); │ │ &set_freq); │ │
│ │ } │ │ } │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐│
│ │ rpcserver_thrift (port 9090) ││
│ │ ┌─────────────────┐ ┌─────────────────┐ ││
│ │ │ setcallbackmap │ │ getcallbackmap │ ││
│ │ │ "blockA::gain" │ │ "blockA::gain" │ ││
│ │ │ "blockB::freq" │ │ "blockB::freq" │ ││
│ │ └─────────────────┘ └─────────────────┘ ││
│ └──────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────┘
│ Thrift Binary Protocol (TCP)
┌──────────────────────────────────────────────────────────────────┐
│ Python Client │
│ from gnuradio.ctrlport import GNURadioControlPortClient │
│ │
│ client = GNURadioControlPortClient(host='localhost', port=9090) │
│ knobs = client.getKnobs(['blockA::gain', 'blockB::freq']) │
│ client.setKnobs({'blockA::gain': 2.5}) │
└──────────────────────────────────────────────────────────────────┘

Enabling ControlPort

~/.gnuradio/config.conf:

[ControlPort]
on = True
edges_list = True
[thrift]
port = 9090
nthreads = 2

Client Example

from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient
# Connect to running flowgraph
client = GNURadioControlPortClient(host='localhost', port=9090)
# Get knobs (read values)
knobs = client.getKnobs(['analog_sig_source_0::frequency'])
print(knobs)
# Set knobs (write values)
client.setKnobs({'analog_sig_source_0::frequency': 1500.0})
# Regex-based retrieval - get all frequency knobs
all_freq_knobs = client.getRe(['.*::frequency'])
# Get metadata (units, min, max, description)
props = client.properties(['analog_sig_source_0::frequency'])
print(props['analog_sig_source_0::frequency'].units)
print(props['analog_sig_source_0::frequency'].min)

GUI Monitoring Tools

  • gr-ctrlport-monitor — Real-time variable inspection
  • gr-perf-monitorx — Performance profiling visualization
Terminal window
gr-ctrlport-monitor localhost 9090
gr-perf-monitorx localhost 9090

Key Files

FilePurpose
gnuradio-runtime/lib/controlport/thrift/gnuradio.thriftThrift IDL definition
gnuradio-runtime/include/gnuradio/rpcserver_thrift.hServer implementation
gnuradio-runtime/include/gnuradio/rpcregisterhelpers.hRegistration templates
gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.pyPython client
gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.pyThrift connection

Comparison: XML-RPC vs ControlPort

FeatureXML-RPCControlPort/Thrift
SetupAdd block to flowgraphEnable in config.conf
ProtocolHTTPBinary TCP
PerformanceSlower (text-based)Faster (binary)
Type supportBasic Python typesComplex, vectors, PMT
MetadataNoneUnits, min/max, hints
ToolingAny HTTP clientSpecialized monitors
Use caseSimple controlPerformance monitoring

When to Use Each

Use XML-RPC when:

  • You need quick, simple remote control
  • Integration with web applications
  • Language-agnostic client access
  • Minimal configuration

Use ControlPort when:

  • You need performance monitoring
  • Working with complex data types
  • Block-level control granularity
  • Need metadata about parameters

GR-MCP Integration

GR-MCP wraps both protocols:

# XML-RPC via GR-MCP
connect_to_container(name="fm-radio")
set_variable(name="freq", value=101.1e6)
# ControlPort via GR-MCP
connect_to_container_controlport(name="fm-profiled")
get_performance_counters()