GNU Radio Companion

Stream Tags

Metadaten, die synchron an einzelne Samples eines Datenstroms geheftet werden — Aufbau, PMTs und Beispielcode.

In GNU Radio fließen Daten normalerweise als ununterbrochener Strom von Samples. Ein Stream Tag ist ein Mechanismus, um Metadaten (in Form von Schlüssel-Wert-Paaren) direkt an ein spezifisches Sample innerhalb dieses Datenstroms zu heften.

Im Gegensatz zu „Message Passing" (das asynchron und ohne Zeitstempel erfolgt) sind Tags synchron: Ein Tag bleibt an "seinem" Sample, während es den Flowgraph durchläuft. Bei ratenverändernden Blöcken (Dezimatoren, Interpolatoren) bedeutet das nicht denselben Offset-Wert, sondern eine proportionale Umrechnung — ein Tag bei Offset 1000 landet nach einer Dezimation um Faktor 10 bei Offset 100, bleibt also an der relativen Position im Signal, nicht am identischen Sample-Index.

Ein Tag besteht technisch aus vier Komponenten:

  1. Offset: Die absolute Position im Datenstrom (Sample-Index).
  2. Key: Ein PMT-Symbol (z.B. pmt::mp("rx_freq")).
  3. Value: Ein PMT-Wert (kann fast alles sein: Zahlen, Strings, Vektoren).
  4. Source ID: Ein PMT-Symbol, das den Block identifiziert, der den Tag erstellt hat.

PMT (Polymorphic Types)

Ein PMT ist ein Container-Typ, der jeden beliebigen Datentyp aufnehmen kann. Er verhält sich ähnlich wie ein Variant-Typ in anderen Sprachen oder ein Object in Java/Python. Das ermöglicht es GNU Radio, Nachrichten und Tags zu versenden, ohne dass der Empfänger-Block vorher genau wissen muss, welche Struktur die Daten haben.

Die meisten PMTs sind immutable. Um einen Wert zu ändern, muss daher technisch gesehen ein neues PMT-Objekt erzeugt werden. Das macht sie sicher für den Einsatz in Multi-Threaded-Umgebungen, da keine Locks benötigt werden, wenn ein Tag von mehreren Blöcken gleichzeitig gelesen wird. Ein in Python erstelltes PMT-Element kann auch direkt in einem C++-Block gelesen werden. Das ermöglicht eine nahtlose Datenübergabe.

Python verfügt über ein PMT-Modul, mit dessen Hilfe die PMT-Variablen erzeugt werden können.

Bsp.: pmt.from_long(42)
Bsp.: pmt.from_long(42)

Tags einfügen

In Verbindung mit den Stream Tags können Tags sowohl als Keys als auch als Values angelegt werden.

self.key_preamble = pmt.intern("preamble_start") wäre z.B. ein Key, um den Start einer Preamble zu markieren.

Ein Tag wird mit der Methode self.add_item_tag() in einen Datenstrom eingespeist.

self.add_item_tag(stream_id, offset, key, value)

Bsp.: self.add_item_tag(0, self.nitems_written(0) + index, self.key_preamble, pmt.PMT_T)
self.add_item_tag(stream_id, offset, key, value)

Bsp.: self.add_item_tag(0, self.nitems_written(0) + index, self.key_preamble, pmt.PMT_T)

Tags auslesen

Das Gegenstück zum Einfügen ist das Auslesen: Ein nachfolgender Block fragt mit self.get_tags_in_window() alle Tags ab, die innerhalb des aktuell verarbeiteten Samples-Fensters liegen. Ohne diesen Schritt bleiben eingefügte Tags im Datenstrom, aber kein Block reagiert auf sie.

def work(self, input_items, output_items):
    n_input = len(input_items[0])

    # Alle Tags im aktuellen Verarbeitungsfenster abrufen
    tags = self.get_tags_in_window(0, 0, n_input, self.key_preamble)

    for tag in tags:
        offset_in_window = tag.offset - self.nitems_read(0)
        # z.B. Symbol-Sync zurücksetzen, sobald die Preamble erkannt wird
        print(f"Preamble-Tag gefunden bei relativer Position {offset_in_window}")

    output_items[0][:] = input_items[0]
    return n_input
def work(self, input_items, output_items):
    n_input = len(input_items[0])

    # Alle Tags im aktuellen Verarbeitungsfenster abrufen
    tags = self.get_tags_in_window(0, 0, n_input, self.key_preamble)

    for tag in tags:
        offset_in_window = tag.offset - self.nitems_read(0)
        # z.B. Symbol-Sync zurücksetzen, sobald die Preamble erkannt wird
        print(f"Preamble-Tag gefunden bei relativer Position {offset_in_window}")

    output_items[0][:] = input_items[0]
    return n_input

tag.offset ist dabei immer die absolute Position im gesamten Stream — self.nitems_read(0) liefert die absolute Position des ersten Samples im aktuellen Fenster, die Differenz ergibt die Position relativ zum gerade verarbeiteten Puffer.