Ein Frontpanel für den USB-Empfänger

Der USB-Sprachempfänger funktioniert, sieht aber aus wie ein generischer GRC-Flowgraph — Regler und Spektrumanzeigen, kein Empfangsgerät. Dieser Artikel sammelt, womit sich das ändern lässt.

Der USB-Sprachempfänger für 144,300 MHz demoduliert korrekt, sieht aber wie jeder andere GRC-Flowgraph aus: Schieberegler übereinander, zwei Spektrumplots, fertig. Ein echtes Empfangsgerät hat ein S-Meter, eine Signalanzeige, ein Bedienfeld mit fester Anordnung. Dieser Artikel bestandsaufnimmt, was GNU Radio Companion dafür von Haus aus mitbringt, und was jenseits davon möglich ist — als Grundlage für die spätere Umsetzung.

1. Was GNU Radio Companion nativ mitbringt

Neben den bereits im Empfänger genutzten Elementen (QT GUI Range-Schieberegler, QT GUI Freq Sink-Spektrumplots) bringt GRC 3.10 eine ganze Reihe weiterer QT-Widgets mit, die deutlich mehr nach Empfangsgerät aussehen:

Block Aussehen Typischer Einsatz im Empfänger
QT GUI Dial Gauge kreisförmige Anzeige mit gefülltem Bogen S-Meter (Signalstärke)
QT GUI Level Gauge Balkenanzeige (Fortschrittsbalken-Stil), vertikal oder horizontal Aussteuerungsanzeige, alternative S-Meter-Form
QT GUI LED Indicator farbiger Kreis, an/aus „Signal vorhanden" bzw. Squelch-Status
QT GUI Dial (Dial Control) Drehknopf als Eingabeelement Lautstärke oder Feinabstimmung, statt Schieberegler
QT GUI Toggle Switch / Toggle Button Kippschalter-Optik Ein/Aus-artige Funktionen (z. B. Squelch aktivieren)
QT GUI Number Sink digitale Zahlenanzeige Frequenz-Digitalanzeige
QT GUI Tab Widget Reiter/Tabs mehrere „Seiten" wie bei einem echten Gerätemenü (Hauptbedienung / Spektrum / Einstellungen)
QT GUI App Background Hintergrundbild/-farbe für das gesamte Fenster Gehäusegrafik hinter den Bedienelementen

Alle Anzeige-Widgets (Dial Gauge, Level Gauge, LED) haben eine feste Farbpalette (14 benannte Farben wie green, amber fehlt allerdings — am nächsten kommt orange/yellow), keine freien Hex-Werte — für ein grobes Retro-Empfänger-Design reicht das meist, für ein exaktes Markenlayout nicht.

2. Live-Werte in die Anzeige bekommen

Dial Gauge und Level Gauge erwarten ihren Wert nicht als Signal-Stream, sondern als GRC-Variable oder Message — das Signal muss also erst in eine periodisch aktualisierte Zahl umgewandelt werden. Das Standardmuster dafür kombiniert zwei Blöcke:

  1. Probe Avg Mag^2 (analog.probe_avg_mag_sqrd_f) hängt am Signalpfad und stellt eine .level()-Methode bereit, die die aktuelle (geglättete) Signalleistung zurückgibt.
  2. Function Probe (variable_function_probe) ruft diese Methode in einem eigenen Thread mit fester Rate (z. B. 10 Hz) ab und schreibt das Ergebnis in eine GRC-Variable — genau diese Variable trägt man dann als value in den Dial Gauge ein.
Verifiziert (GNU Radio 3.10.12.0, tatsächlich kompiliert und gestartet): Ein minimaler Testaufbau — Sinusquelle → Probe Avg Mag^2 → Function Probe → Dial Gauge (S-Meter) + LED-Indikator (leuchtet bei Pegel > 0,05) + App-Background mit dem eigenen sdr-hintergrund.png als Fenster-Hintergrund — kompiliert fehlerfrei und läuft ohne Laufzeitfehler. Auszug aus dem von grcc generierten Code:
self.qtgui_ledindicator_0 = qtgui.GrLEDIndicator('Signal', "green", "red", level_var > 0.05, 40, 1, 1, 1, self)
...
bkgnd = "background-image: url(" + '/pfad/zu/sdr-hintergrund.png' + "); background-repeat: repeat_n; background-position: center;"
self.setStyleSheet(styleSht)
...
_qtgui_dialgauge_0_lg_win = qtgui.GrDialGauge('S-Meter', "green", "black", "white", 0, 1, 150, 1, isFloat, True, True, self)
self.qtgui_ledindicator_0 = qtgui.GrLEDIndicator('Signal', "green", "red", level_var > 0.05, 40, 1, 1, 1, self)
...
bkgnd = "background-image: url(" + '/pfad/zu/sdr-hintergrund.png' + "); background-repeat: repeat_n; background-position: center;"
self.setStyleSheet(styleSht)
...
_qtgui_dialgauge_0_lg_win = qtgui.GrDialGauge('S-Meter', "green", "black", "white", 0, 1, 150, 1, isFloat, True, True, self)

Der LED-Zustand (level_var > 0.05) wird dabei direkt als Python-Ausdruck in den Parameter eingetragen — GRC übernimmt beliebige Ausdrücke, nicht nur einfache Variablennamen.

3. Globales Reskinning: Stylesheet und Hintergrundbild

Zwei unabhängige Hebel für den Gesamteindruck:

  • qt_qss_theme (Parameter des Options-Blocks, bislang in unseren Projekten leer): Pfad zu einer externen .qss-Datei (Qt Style Sheets, syntaktisch an CSS angelehnt) — damit lassen sich Farben, Schriftarten und Rahmen aller Widgets der Anwendung auf einen Schlag umstellen, z. B. auf ein dunkles Thema mit heller Digitalanzeige-Schrift.
  • QT GUI App Background: setzt gezielt ein Hintergrundbild (und/oder eine Hintergrundfarbe) für das gesamte Fenster — genau der Mechanismus, mit dem sich eine Gehäusegrafik hinter die Bedienelemente legen ließe. Für den USB-Empfänger böte sich unser bereits vorhandenes sdr-hintergrund.png als Ausgangsmaterial an.

4. Layout: GUI Hint und Tab Widget

Jedes QT-Widget hat einen GUI Hint-Parameter der Form [tab_id@tab_index]: [row, col, row_span, col_span] — exakt damit ist im Empfänger bereits die feste Anordnung der beiden Spektrumplots (0,0,1,2 bzw. 1,0,1,2) gesetzt. Mit QT GUI Tab Widget lässt sich zusätzlich in mehrere Reiter gliedern — z. B. ein Reiter „Empfang" mit S-Meter/LED/Reglern und ein separater Reiter „Spektrum" mit den Frequenzanzeigen, näher am Bedienkonzept eines echten Empfängers als eine einzige, immer volle Fläche.

5. Grenzen der nativen QT-Widgets

  • Feste Formen: Der Dial Gauge zeichnet einen gefüllten Bogen, keine frei gestaltbare Zeigernadel-Grafik — für einen klassischen Zeiger-S-Meter-Look reicht das nicht.
  • Begrenzte Farbpalette: 14 benannte Farben, keine beliebigen Hex-/RGB-Werte in den Standardparametern.
  • Ein Hintergrundbild fürs ganze Fenster, nicht pro Widget — einzelne Regler lassen sich nicht individuell mit eigener Grafik hinterlegen.
  • Kein Vektor-/SVG-Rendering der Bedienelemente selbst — es bleiben Qt-Standardwidgets mit angepassten Farben, kein eigenes Icon-/Skin-System.

6. Alternativen jenseits von GRC

Ansatz Gestaltungsfreiheit Aufwand Anmerkung
Eigene PyQt5/PySide-App über ZMQ hoch (beliebige Widgets, SVG, eigene Zeigergrafiken) mittel Präzedenzfall bereits im eigenen Repo: der DCF77-ZMQ-Client empfängt Werte von einem GRC-Flowgraph und stellt sie in einer komplett eigenen PyQt5-Oberfläche dar — dasselbe Muster ließe sich auf den USB-Empfänger übertragen
Web-Frontend (Browser, HTML/CSS/SVG/Canvas) über WebSocket-Bridge sehr hoch (CSS-Animationen, responsives Design) hoch erfordert einen dauerhaft laufenden Backend-Prozess, der die Werte per WebSocket an den Browser weiterreicht
Kivy statt Qt hoch, moderneres/animierbares Widget-System mittel bis hoch eigenständige App, GRC nur noch als reine Signalverarbeitungs-Bibliothek genutzt, keine GRC-generierte Oberfläche mehr — Schritt 1 bereits umgesetzt: Kivy-Frontpanel
Fertige SDR-Konsolen (SDR++, GQRX, CubicSDR) keine eigene — vorgegebene Oberfläche keiner kein eigener Flowgraph mehr dahinter, nur als Vorbild/Inspiration relevant

7. Empfehlung für die Umsetzung

Zwei sinnvoll trennbare Ausbaustufen:

  1. Schneller Weg, native QT-Widgets: App-Background mit Gehäusegrafik, Dial Gauge als S-Meter (gespeist über Probe + Function Probe wie in Abschnitt 2), LED-Indikator für den Squelch-Status, Tab Widget zur Gliederung — alles direkt im bestehenden ssb_usb_144300.grc ergänzbar, keine neue Laufzeitumgebung nötig.
  2. Aufwendiger, aber freier: eigene ZMQ-App — der Empfänger sendet Messwerte (Signalpegel, Squelch-Status, Frequenz) per ZMQ, eine separate PyQt5-Anwendung nach dem Vorbild des DCF77-Displays zeichnet ein vollständig frei gestaltetes Empfänger-Frontpanel, inklusive echter Zeigernadel-Grafik.
  3. Dritter Weg, schrittweiser Umbau in Kivy — GRC bleibt nur noch für die reine Signalverarbeitung im Einsatz, die komplette Oberfläche wird Widget für Widget in Kivy nachgebaut. Schritt 1 (Frequenzanzeige + Spektrum) ist bereits umgesetzt und dokumentiert im eigenen Artikel Kivy-Frontpanel: Frequenzanzeige + Spektrum — dort insbesondere Abschnitt 2 für die Kommunikation zwischen GRC und Kivy im Detail.