Your First Flowgraph
This tutorial walks through building a simple FM receiver flowgraph programmatically. You’ll learn the core workflow: create blocks, set parameters, connect ports, validate, and save.
What We’re Building
A basic FM receiver chain:
osmosdr_source → low_pass_filter → analog_wfm_rcv → audio_sinkThis receives RF at a configurable frequency, filters it, demodulates FM, and outputs audio.
Step-by-Step
-
Create the source block
make_block(block_type="osmosdr_source")# Returns: "osmosdr_source_0"GR-MCP automatically assigns unique names by appending
_0,_1, etc. -
Set source parameters
First, inspect available parameters:
get_block_params(block_name="osmosdr_source_0")Then configure:
set_block_params(block_name="osmosdr_source_0", params={"freq": "101.1e6", # 101.1 MHz FM station"sample_rate": "2e6", # 2 MHz sample rate"gain": "40", # RF gain"args": '""' # Auto-detect device}) -
Create the filter
make_block(block_type="low_pass_filter")set_block_params(block_name="low_pass_filter_0", params={"type": "fir_filter_ccf","decim": "10", # Decimate by 10 → 200 kHz"cutoff_freq": "100e3", # 100 kHz cutoff"transition_width": "10e3","win": "window.WIN_HAMMING"}) -
Create the FM demodulator
make_block(block_type="analog_wfm_rcv")set_block_params(block_name="analog_wfm_rcv_0", params={"quad_rate": "200e3", # Input rate after decimation"audio_decimation": "4" # Output at 50 kHz}) -
Create the audio output
make_block(block_type="audio_sink")set_block_params(block_name="audio_sink_0", params={"samp_rate": "48000","device_name": '""' # Default audio device}) -
Connect the blocks
connect_blocks(source_block_name="osmosdr_source_0",sink_block_name="low_pass_filter_0",source_port_name="0",sink_port_name="0")connect_blocks(source_block_name="low_pass_filter_0",sink_block_name="analog_wfm_rcv_0",source_port_name="0",sink_port_name="0")connect_blocks(source_block_name="analog_wfm_rcv_0",sink_block_name="audio_sink_0",source_port_name="0",sink_port_name="0") -
Validate the flowgraph
validate_flowgraph()# Returns: True# Check for any warningsget_all_errors()# Returns: [] -
Save the flowgraph
save_flowgraph(filepath="/tmp/fm_receiver.grc")You can now open this in GNU Radio Companion!
Generate Python Code
Instead of saving as .grc, you can generate executable Python directly:
result = generate_code(output_dir="/tmp")# Returns GeneratedCodeModel with:# - file_path: "/tmp/fm_receiver.py"# - is_valid: True# - warnings: []Using Variables
GR-MCP supports flowgraph variables for runtime tuning. Set them via flowgraph options:
set_flowgraph_options(params={ "title": "FM Receiver", "author": "Your Name", # Variables are defined here too})
# Or use the expression evaluator to test valuesevaluate_expression("101.1e6 + 200e3") # Returns: 101300000.0Complete Script
Here’s the full example as a Python script:
#!/usr/bin/env python3"""Build an FM receiver with GR-MCP."""
import asynciofrom fastmcp import Client
async def build_fm_receiver(): # Connect to GR-MCP (running as MCP server) async with Client("gr-mcp") as client: # Create blocks await client.call_tool("make_block", {"block_type": "osmosdr_source"}) await client.call_tool("make_block", {"block_type": "low_pass_filter"}) await client.call_tool("make_block", {"block_type": "analog_wfm_rcv"}) await client.call_tool("make_block", {"block_type": "audio_sink"})
# Configure source await client.call_tool("set_block_params", { "block_name": "osmosdr_source_0", "params": { "freq": "101.1e6", "sample_rate": "2e6", "gain": "40" } })
# Configure filter await client.call_tool("set_block_params", { "block_name": "low_pass_filter_0", "params": { "type": "fir_filter_ccf", "decim": "10", "cutoff_freq": "100e3", "transition_width": "10e3" } })
# Connect signal chain for src, dst in [ ("osmosdr_source_0", "low_pass_filter_0"), ("low_pass_filter_0", "analog_wfm_rcv_0"), ("analog_wfm_rcv_0", "audio_sink_0"), ]: await client.call_tool("connect_blocks", { "source_block_name": src, "sink_block_name": dst, "source_port_name": "0", "sink_port_name": "0" })
# Validate and save result = await client.call_tool("validate_flowgraph", {}) print(f"Valid: {result.data}")
await client.call_tool("save_flowgraph", { "filepath": "/tmp/fm_receiver.grc" })
if __name__ == "__main__": asyncio.run(build_fm_receiver())Next Steps
- Running in Docker — Launch the flowgraph with runtime control
- OOT Modules — Add gr-osmosdr and other modules