Skip to content

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_sink

This receives RF at a configurable frequency, filters it, demodulates FM, and outputs audio.

Step-by-Step

  1. 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.

  2. 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
    })
  3. 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"
    })
  4. 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
    })
  5. 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
    })
  6. 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"
    )
  7. Validate the flowgraph

    validate_flowgraph()
    # Returns: True
    # Check for any warnings
    get_all_errors()
    # Returns: []
  8. 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 values
evaluate_expression("101.1e6 + 200e3") # Returns: 101300000.0

Complete Script

Here’s the full example as a Python script:

#!/usr/bin/env python3
"""Build an FM receiver with GR-MCP."""
import asyncio
from 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