Youssef Mahmoud. Graphical interoperability between CyberEther and GNU Radio Mentors: Luigi Cruz , Håkon Vågsether Code: github.com/joeonit/gr-cyberether Weekly blog: joeonit.github.io

Overview

gr-cyberether is a GNU Radio Out-of-Tree (OOT) module that provides high-performance, modern visualization capabilities by relying on CyberEther rendering and visualization engine. The module is built using C++ and integrates with the GNU Radio ecosystem.

Deliverables

  Proposal Outcome
D0 Module scaffold, CyberEther discovery, GNU Radio main and 3.10 compatibility Done
D1 Data-ingest path, investigate a custom cyber_buffer Done. Custom buffer investigated and rejected — why
D2 cyber_context lifecycle manager Done, but not the design the proposal specified
D3 Line plot + waterfall sinks, Python QA Done
D4 GRC integration Done
More constellation sink, spectrum analyzer and cyber_range for slider control and windows support Done

Where the work is

66 commits on main, 105 files, 10 pull requests.

PR    
#1 Merged Minimal lineplot sink + examples
#2 Merged SPSC ring buffer
#3 Merged cyber_context, dropping the ring buffer
#4 Merged Waterfall sink
#5 Merged Constellation sink
#6 Open cyber_range — blocked on CyberEther #289
#7 Merged Require CyberEther 1.7.0, test against 1.9.1
#8 Merged Windows release workflow
#9 Merged Reproducible Windows build
#10 Open Spectrum analyzer sink

Also upstream, in CyberEther itself

PR    
#216 Merged feat: expose scatter/constellation plot type through superluminal
#289 Open feat(superluminal): add GlobalInterface for controls above the mosaic

More details

Four sinks :

  • cyber_lineplot_sink — line plot, time or frequency
  • cyber_waterfall_sink — scrolling waterfall
  • cyber_constellation_sink — IQ scatter
  • cyber_spectrum_analyzer_sink — spectrum line over a waterfall, one shared FFT

Plus cyber_range, a slider drawn inside the window that retargets a GRC variable while the flowgraph runs.

The CyberEther sink family

cyber_context

The cyber_context class is the central coordinator for gr-cyberether. It acts as a singleton that manages the registration of plot requests from GNU Radio sink blocks and orchestrates the lifecycle of the Superluminal rendering engine.

cyber_context: each sink writes its own tensor in place; Superluminal reads that same memory zero-copy

Source: lib/cyber_context.cc · include/gnuradio/cyberether/cyber_context.h

Mosaic Layout

Once more than one sink could exist, they had to share a window without knowing about each other. Superluminal lays plots on a mosaic, so the question was how a GRC user says where a plot goes. Rather than invent a syntax, sinks take a GUI Hint in the one GNU Radio users already know:

"row, col"                      one cell
"row, col, row_span, col_span"  a rectangle

The translation lives in lib/mosaic_layout.{h,cc}compute_layout(hints) in, grid and per-sink rects out. It is a pure function of its inputs, so it needs no GPU, no window and no flowgraph to exercise.

sink               hint            resulting mosaic
─────────────────────────────────────────────────────
constellation      "0, 0, 2, 1"     ┌───┬───┬───┐
scope              "0, 1"           │ 0 │ 1 │ 2 │
spectrum           "0, 2"           │   ├───┴───┤
waterfall          "1, 1, 1, 2"     │ 0 │   3   │
magnitude          "2, 0, 1, 3"     ├───┴───┴───┤
                                    │     4     │
                                    └───────────┘

Source: lib/mosaic_layout.cc · lib/mosaic_layout.h

Controls

cyber_range is the GNU Radio qtgui_range equivalent, a slider inside the CyberEther window that retargets a flowgraph variable live.

It needed somewhere to draw that wasn’t a mosaic cell, because controls should not eat a plot slot. so I added one upstream: Superluminal::GlobalInterface(), a full-width panel above the mosaic whose height is measured and reserved so it never overlaps the grid.

cyber_range sliders in the control panel above the mosaic

Packaging and support

  • CI builds against GNU Radio 3.10 and main, CyberEther pinned by ref and cached
  • Tagged releases publish archives for Linux x86_64, macOS arm64 and Windows x86_64, with libjetstream bundled and rpath rewritten — no CyberEther install needed
  • packaging/install.sh inside each archive finds your GNU Radio, checks compatibility, installs, and verifies
  • Examples install to share/gnuradio/examples/cyberether, including an FM receive chain and a QPSK receiver with sync

Notable bugs, Code that is deleted or not yet done

Getting a window out of GRC The hardest integration problem, and invisible when it works. CyberEther needs the main thread’s event loop, and a generated top_block doesn’t expect to hand it over. The easy answer is telling every user to paste in a Python Snippet block, which wasn’t optimal.Instead the module ships its custom GRC workflow cyberether_standalone_gui.workflow.yml plus a generator module and Mako template. Pick it in the Options block and the generated top_block starts the flowgraph and calls cyberether.present() on the main thread.

Windows problems Five CI runs, five failures, each a genuinely different cause: Git’s link.exe shadowing MSVC’s, the Vulkan SDK installed but not on INCLUDE, PKG_CONFIG_PATH joined with a Unix : on a platform whose paths contain D:\, pkgconf treating backslashes as escapes, and conda activate silently overwriting PKG_CONFIG_PATH at the start of every step. None of these are findable without executing on a real Windows runner. It passes now and publishes an archive.

The deleted ring buffer (#2, removed in #3). Solved a synchronisation problem that zero-copy in-place reads mean doesn’t exist. Understand the consumer’s contract before optimising the producer.

Not adopted: the custom cyber_buffer from D1. The gr-cuda template exists, but it would remove a copy that isn’t happening. Worth revisiting only if a sink ever needs data to stay device-side.

Not done

  • Spectrum analyzer sink is not merged. Complete — block, bindings, GRC YAML, 5 passing QA tests, working demo but it needs a Type::SpectrumAnalyzer that is on an unreleased CyberEther branch. It merges when that ships.
  • PR #6 is open, waiting on CyberEther #289.

Acknowledgments

I am very thankful to my mentors, Luigi Cruz and Håkon Vågsether. They have been incredibly responsive, consistently guiding me and answering my questions. Also, my interactions with the community on Matrix have been very welcoming and supportive.