GNU Radio Companion

Daten in GRC

Woher Daten im Flowgraph stammen, wie sie gepuffert werden und wie der Scheduler den Datenfluss steuert.

Gnu Radio Companion unterstützt unterschiedliche Datentypen, die je nach Verarbeitungszweck bereitgestellt werden.

Über den Source Block wird festgelegt, woher die Ausgangsdaten stammen sollen. Folgende Sources kommen dabei zum Einsatz:

  1. Selbst erzeugte Signalformen. Verschiedene Signalformen wie Sinus, Dreieck oder Rechteck Signale können in GRC selbst erzeugt werden. Diese Signale lassen sich mit anderen Signalen überlagern, um bestimmte Effekte zu erzeugen.
  2. Aufgezeichnete Signale. Es ist möglich, Signale aufzuzeichnen und als WAV-File abzuspeichern. Das ist insbesondere dann interessant, wenn man verschiedene Demodulationstechniken erlernen will, ohne über eigene Signalquellen zu verfügen. Im SigWiki ist eine Vielzahl von „Konserven" verfügbar, die so analysiert werden können.
  3. Signale empfangen. Für den Empfang eigener Signale werden verschiedene SDR-Hardwarekomponenten unterstützt.

Der Hardware Source Block gibt Daten normalerweise in Form von komplexen I/Q-Samples aus, wobei jedes Sample eine Kombination von In-Phase (I) und Quadrature (Q)-Komponenten ist. Die Daten werden mit einer eingestellten Abtastrate ausgegeben, die bestimmt, wie viele Samples pro Sekunde erfasst und verarbeitet werden.

Der WAV Block dagegen liefert Floating-Point-Werte. Bei einer Mono-Datei ist das ein einzelner Float-Stream. Eine verbreitete Praxis für IQ-Aufnahmen ist jedoch eine Stereo-WAV-Datei, bei der der linke Kanal die I-Komponente und der rechte Kanal die Q-Komponente trägt — der WAV-Source-Block liefert dann zwei getrennte Float-Streams, die erst über einen Float To Complex-Block zu einem komplexen IQ-Stream zusammengeführt werden müssen. Bei den internen Signalquellen kann der Typ der Ausgabewerte direkt eingestellt werden.

Datenfluss im Überblick

graph LR
    subgraph Source["Quelle (z.B. SDR/WAV)"]
        A[Hardware Source]
    end

    subgraph Buffer1["Buffer (Memory)"]
        B1[Items I/Q]
    end

    subgraph Processing["Verarbeitungs-Block"]
        P[work-Funktion]
    end

    subgraph Buffer2["Buffer (Memory)"]
        B2[Verarbeitete Daten]
    end

    subgraph Sink["Ziel (z.B. Audio/Plot)"]
        S[Sink Block]
    end

    A --> B1
    B1 --> P
    P --> B2
    B2 --> S

    style B1 fill:#f9f,stroke:#333,stroke-width:2px
    style B2 fill:#f9f,stroke:#333,stroke-width:2px

Buffer

GNU Radio verwendet Buffer, um die Daten, die zwischen den Blöcken übertragen werden, zu speichern und weiterzugeben. Jedes Mal, wenn ein Block ein Signal verarbeitet, schreibt er das Ergebnis in einen Puffer, der dann von nachgeschalteten Blöcken gelesen wird.

Der Buffer dient als Zwischenspeicher für die Daten und ermöglicht es GNU Radio, asynchron zu arbeiten, wobei jeder Block so schnell arbeitet, wie er kann, unabhängig von den anderen.

Diese Pufferung ist auch notwendig, um Daten zwischen Blöcken mit unterschiedlicher Verarbeitungsgeschwindigkeit zu synchronisieren.

Die Größe der Puffer in GNU Radio ist variabel und wird vom System dynamisch verwaltet — sie richtet sich nach der Item-Größe und wird als Vielfaches der Betriebssystem-Page-Size angelegt, standardmäßig in einer Größenordnung von rund 32.768 Items.

Kein Datenverlust durch Überschreiben: GNU Radios Puffer arbeiten mit Flow Control (Backpressure), nicht durch Überschreiben unverarbeiteter Daten. Ist der Ausgabepuffer eines Blocks voll, weil der nachfolgende Block noch nicht genug gelesen hat, wird der produzierende Block einfach angehalten (sein work() wird vom Scheduler nicht aufgerufen), bis wieder Platz frei ist. Unverarbeitete Daten gehen dabei nie verloren — sonst würde jede Signalkette bei ungleich schnellen Blöcken stillschweigend Daten verlieren. Der Begriff „Ringpuffer" bezieht sich stattdessen auf einen Implementierungstrick: GNU Radio bildet denselben physischen Speicherbereich zweimal hintereinander im virtuellen Adressraum ab, sodass Lese-/Schreibzugriffe über die Pufferende hinaus ohne Sonderbehandlung als zusammenhängender Speicher erscheinen.

Der zeitliche Ablauf dieser Backpressure sieht so aus, wenn ein konsumierender Block langsamer arbeitet als der produzierende:

sequenceDiagram
    participant S as Scheduler
    participant B1 as Produzierender Block
    participant Buf as Buffer (Voll)
    participant B2 as Konsumierender Block

    Note over B2: Arbeitet langsam...
    B2->>Buf: Liest Daten
    Buf->>S: Pufferkapazität frei
    S->>B1: Trigger work()
    B1->>Buf: Schreibt neue Daten
    Note over Buf: Buffer erreicht Limit
    Buf-->>S: Signal: "Full"
    S->>B1: Stoppe Ausführung (Backpressure)
    Note over B1: Block pausiert

Scheduler

Der Scheduler in GNU Radio ist dafür verantwortlich, dass die Blöcke im Flowgraph in der richtigen Reihenfolge und mit der richtigen Geschwindigkeit arbeiten. Der Scheduler stellt sicher, dass:

  • Blöcke Daten aus den Puffern lesen, sobald sie verfügbar sind.
  • Blöcke nur dann schreiben, wenn der Puffer des nächsten Blocks bereit ist, neue Daten zu empfangen.

Der Scheduler verwaltet dabei die Kommunikation zwischen den Blöcken so, dass keine Daten verloren gehen und dass Blöcke mit unterschiedlichen Verarbeitungsanforderungen dennoch effizient miteinander arbeiten.