Field Guide · concept

Also known as: DSP block, GNU Radio block, signal processing block

A signal-processing block is a self-contained DSP unit with typed input and output signatures and a work() method the runtime calls to turn input samples into output samples.1 It is the node in a flowgraph: one block does one job — a filter, a resampler, a demodulator, a decoder — and exposes just enough interface for a scheduler to hand it buffers of samples and collect the results. Blocks are the reusable Lego bricks of block-based SDR: wire different ones together and you get a different radio.

block work(in, out) input stream (N items) output stream (M items)
A block declares its input and output item types (io signatures) and implements work(), which consumes input items and produces output items.

How it works

Every block declares two things up front and implements one behaviour:

  • Input signature — how many input ports it has and the item size on each (complex 8-byte IQ, 4-byte float, 1-byte packed bits, a custom struct).
  • Output signature — the same for its outputs. A source has zero inputs; a sink has zero outputs.
  • A work function — the code the runtime calls with pointers to a chunk of available input and space for output. In GNU Radio, a sync block (one output item per input item) implements work(); a general block with a different or data-dependent rate implements general_work() plus a forecast() that tells the scheduler how many input items it needs to produce a given number of outputs.

The block returns how many output items it actually produced, and the runtime advances the stream pointers accordingly. This contract — here is your input, here is your output space, tell me how much you consumed and produced — is what lets the scheduler manage buffering, back-pressure, and rate changes without the block author having to know anything about the blocks around it.

In practice

Rate relationships classify the common block types:

  • Sync block — 1:1. A per-sample gain, an FM discriminator, an AGC.
  • Decimator — N:1. Consumes N inputs per output; the front of a channelizer.
  • Interpolator — 1:N. Upsampling before pulse shaping.
  • General block — arbitrary or data-dependent rate. A packet framer that emits a burst only when a valid frame is found uses general_work() and forecast().

Blocks can also carry stream tags — metadata pinned to a sample index — and message ports for asynchronous control, so a block is not limited to pushing samples: it can annotate the stream or exchange out-of-band messages. Custom blocks that ship outside the core tree live in an out-of-tree module, and a group of blocks can be bundled as a hierarchical block that itself looks like one block.

Relevance to SDR

The block is the unit of reuse in SDR software. Because its interface is narrow and typed, a filter written once works in any flowgraph that feeds it the right item type, and you can unit test a block by feeding known input and asserting on the output — no radio hardware required. This is why GNU Radio ships hundreds of stock blocks and why a large fraction of SDR development is really “write one new block and drop it into an existing chain.”

When you build SDR software, the discipline the block interface enforces — one job, explicit input/output types, no assumptions about neighbours — is worth adopting even without a formal block runtime. GopherTrunk is written in pure Go and does not use GNU Radio, but its decode chain is factored the same way: the down-converter, demodulator, symbol slicer, and framer are each independent stages with clear input and output types, which is exactly what makes them individually testable against captured IQ files. The naming differs; the “one block, one job, typed I/O” principle is identical.

Sources

  1. Blocks Coding Guide — GNU Radio Wiki, on io signatures, sync blocks, general blocks, work()/general_work(), and forecast(). 

See also