Eigene Blöcke

Erste Schritte zur Erstellung eigener , wiederverwertbarer GnuRadio Blöcke

In Gnuradio kann man eigene Blöcke erstellen, um die Funktionalität des Senders oder Empfängers zu erweitern.
Der embedded_python Block kann dazu genutz werden, Python Code in einen Flowgraph zu integrieren. Der grc ledgt diesen Block nach einer eigenen Namenskonvention ab. Möchte man einen Block in verschiedenen Projekten verwenden, ist es sinnvoller, einen eigenen Block zu erstellen. Dieser wird in den eigenen Libs unter den Projekten archiviert.

Der Baudot Decoder Block aus dem DDK9projekt ist ein Beispiel für einen solchen Block.

Dieser Artikel beschreibt die Vorgehensweise, wie man aus einem embeded Block eine wiederverwendbare Komponente macht.

gr-modtool

Bevor wir starten, ein kurzer Blick auf das wichtigste Werkzeug: gr-modtool. Da GNU Radio-Module eine strikte Ordnerstruktur und komplexe CMake-Verbindungen benötigen, automatisiert dieses CLI-Tool das Erstellen, Hinzufügen und Löschen von Blöcken.

Hier sind die drei Kernbefehle, die du für die Arbeit mit eigenen Bibliotheken brauchst:

gr_modtool newmod
Erstellt das äußere Entwicklungsverzeichnis (z. B. gr-meinelib). Es legt die grundlegende Verzeichnisstruktur an (python/, grc/, lib/, include/) und generiert die zentralen CMakeLists.txt-Dateien.

gr_modtool add [optionen]
Fügt einen neuen Block (Code + YAML-Schnittstelle) in ein bestehendes Modul ein. Es verknüpft die neue Datei automatisch in den CMake-Dateien, damit sie später mitkompiliert wird.

gr_modtool rm
Löscht einen Block sauber aus dem Modul und entfernt alle dazugehörigen Einträge aus den CMake-Konfigurationen (wichtig, da man sonst beim Kompilieren Fehler wegen "fehlender Dateien" bekommt).

Das Grundgerüst erstellen

Zuerst erstellen wir das Grundgerüst für deine persönliche Library. Öffne ein Terminal in deinem gewünschten Entwicklungsordner:

# 1. Neues Modul mit dem Namen "meinelib" anlegen
gr_modtool newmod meinelib

# 2. In das neu entstandene Verzeichnis wechseln
cd gr-meinelib
# 1. Neues Modul mit dem Namen "meinelib" anlegen
gr_modtool newmod meinelib

# 2. In das neu entstandene Verzeichnis wechseln
cd gr-meinelib

Den Python Block hinzufügen

gr_modtool add -t python -l python ita2_decoder
gr_modtool add -t python -l python ita2_decoder

Bei diesem Schritt werden die In- und Output Parameter für den Block über eine YAML Datei festgelegt, dies können spätere Nutzer des Blocks ändern, ohne den Quellcode zu öffnen. Es ist deshalb wichtig, dass du dir im Vorfeld Gedanken über die nötigen Parameter und deren sinnvollen Standardwerte machst.

Als Blocktyp währen wir für den Baudot Decoder den sync Block.

Der Code

import numpy as np
from gnuradio import gr

class ita2_decoder(gr.sync_block):
    """
    ITA2 (Baudot) Decoder Block
    """
    def __init__(self, samples_per_symbol=1):
        gr.sync_block.__init__(
            self,
            name='ITA2 Decoder',
            in_sig=[np.uint8],   # Eingang: z.B. Serialisierte Bits/Bytes
            out_sig=[np.uint8]   # Ausgang: Dekodierte ASCII/UTF-8 Zeichen
        )
        self.sps = samples_per_symbol

    def work(self, input_items, output_items):
        in0 = input_items[0]
        out = output_items[0]
        
        # ----------------------------------------------------
        # DEINE ITA2-DEKODIERUNGSLOGIK HIER EINFÜGEN
        # ----------------------------------------------------
        # Beispiel: Kopiert Daten nur durch
        out[:len(in0)] = in0 
        
        return len(out)
import numpy as np
from gnuradio import gr

class ita2_decoder(gr.sync_block):
    """
    ITA2 (Baudot) Decoder Block
    """
    def __init__(self, samples_per_symbol=1):
        gr.sync_block.__init__(
            self,
            name='ITA2 Decoder',
            in_sig=[np.uint8],   # Eingang: z.B. Serialisierte Bits/Bytes
            out_sig=[np.uint8]   # Ausgang: Dekodierte ASCII/UTF-8 Zeichen
        )
        self.sps = samples_per_symbol

    def work(self, input_items, output_items):
        in0 = input_items[0]
        out = output_items[0]
        
        # ----------------------------------------------------
        # DEINE ITA2-DEKODIERUNGSLOGIK HIER EINFÜGEN
        # ----------------------------------------------------
        # Beispiel: Kopiert Daten nur durch
        out[:len(in0)] = in0 
        
        return len(out)

Einbindung in die GRC Oberfläche

id: meinelib_ita2_decoder
label: ITA2 Decoder
category: '[meinelib]'

templates:
  imports: import meinelib
  make: meinelib.ita2_decoder(samples_per_symbol=${samples_per_symbol})

parameters:
- id: samples_per_symbol
  label: Samples per Symbol
  dtype: int
  default: '1'

inputs:
- label: in
  dtype: byte

outputs:
- label: out
  dtype: byte
id: meinelib_ita2_decoder
label: ITA2 Decoder
category: '[meinelib]'

templates:
  imports: import meinelib
  make: meinelib.ita2_decoder(samples_per_symbol=${samples_per_symbol})

parameters:
- id: samples_per_symbol
  label: Samples per Symbol
  dtype: int
  default: '1'

inputs:
- label: in
  dtype: byte

outputs:
- label: out
  dtype: byte

Kompilieren und Einbinden

Zuerst müssen wir die Library bauen und für GRC verfügbar machen. Da das Projekt jetzt die Struktur einer echten Library hat, müssen wir es im Entwicklungsmodus ausführen:

# 1. Im Hauptordner meinelib/ das modul bauen und installieren (User-Scope)
cd meinelib
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/.local
make
make install

# 2. Gruradio-Bibliotheken im Homeverzeichnis neu generieren lassen
grunano --rebuild-grc-library
# 1. Im Hauptordner meinelib/ das modul bauen und installieren (User-Scope)
cd meinelib
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/.local
make
make install

# 2. Gruradio-Bibliotheken im Homeverzeichnis neu generieren lassen
grunano --rebuild-grc-library

Nach diesem Schritt sollte der ITA2 Decoder im GRC-Bibliotheksfenster unter der Kategorie [meinelib] erscheinen und kann wie jeder Standardblock verwendet werden.