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 consoleKey GRC Execution Files
| File | Purpose |
|---|---|
grc/main.py | Entry point |
grc/gui_qt/components/executor.py | ExecFlowGraphThread subprocess launcher |
grc/core/platform.py | Block registry, flowgraph loading |
grc/core/generator/Generator.py | Generator factory |
grc/workflows/common.py | Base generator classes |
grc/workflows/python_nogui/flow_graph_nogui.py.mako | Mako 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.
| Aspect | Details |
|---|---|
| Protocol | HTTP (XML-RPC) |
| Default Port | 8080 |
| Setup | Add XMLRPC Server block to flowgraph |
| Naming | set_varname() / get_varname() |
| Type Support | Basic Python types |
How It Works
- Add
XMLRPC Serverblock to flowgraph - GRC variables automatically become
set_X()/get_X()methods - Connect with any XML-RPC client (Python, C++, curl, etc.)
Client Example
import xmlrpc.client
# Connect to running flowgraphserver = xmlrpc.client.ServerProxy('http://localhost:8080')
# Read and write variablesprint(server.get_freq()) # Read a variableserver.set_freq(145.5e6) # Set a variable
# Flowgraph controlserver.stop() # Stop flowgraphserver.start() # Start flowgraphserver.lock() # Lock flowgraph for modificationsserver.unlock() # Unlock flowgraphKey Files
| File | Purpose |
|---|---|
gr-blocks/grc/xmlrpc_server.block.yml | Server block definition |
gr-blocks/grc/xmlrpc_client.block.yml | Client 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.
| Aspect | Details |
|---|---|
| Protocol | Thrift Binary TCP |
| Default Port | 9090 |
| Setup | Enable in config, blocks call setup_rpc() |
| Naming | block_alias::varname |
| Type Support | Rich (complex, vectors, PMT types) |
| Metadata | Units, 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 = Trueedges_list = True
[thrift]port = 9090nthreads = 2Client Example
from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient
# Connect to running flowgraphclient = 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 knobsall_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
gr-ctrlport-monitor localhost 9090gr-perf-monitorx localhost 9090Key Files
| File | Purpose |
|---|---|
gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift | Thrift IDL definition |
gnuradio-runtime/include/gnuradio/rpcserver_thrift.h | Server implementation |
gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h | Registration templates |
gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py | Python client |
gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.py | Thrift connection |
Comparison: XML-RPC vs ControlPort
| Feature | XML-RPC | ControlPort/Thrift |
|---|---|---|
| Setup | Add block to flowgraph | Enable in config.conf |
| Protocol | HTTP | Binary TCP |
| Performance | Slower (text-based) | Faster (binary) |
| Type support | Basic Python types | Complex, vectors, PMT |
| Metadata | None | Units, min/max, hints |
| Tooling | Any HTTP client | Specialized monitors |
| Use case | Simple control | Performance 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-MCPconnect_to_container(name="fm-radio")set_variable(name="freq", value=101.1e6)
# ControlPort via GR-MCPconnect_to_container_controlport(name="fm-profiled")get_performance_counters()Related Documentation
- Runtime Control Guide — XML-RPC usage in GR-MCP
- ControlPort Monitoring Guide — ControlPort usage in GR-MCP
- GNU Radio docs:
docs/doxygen/other/ctrlport.dox— Block implementation guide