PZF-Demodulation in der Praxis
Ein fertiger GNU-Radio-Flowgraph, der die Phasenmodulation von DCF77 auswertet und bei jedem erkannten Sekundenzeichen eine Meldung ausgibt — Schritt für Schritt erklärt, auch ohne Signalverarbeitungs-Hintergrund.
Dieser Artikel beschreibt einen fertigen GNU-Radio-Flowgraph, der die PZF von DCF77 auswertet — die zusätzliche, feine Phasenmodulation, die dem 77,5-kHz-Träger neben der bekannten Amplitudentastung aufgeprägt ist (Hintergrund dazu in Korrelative Phasenauswertung und Die PZF-Sequenz von DCF77). Der Artikel geht Block für Block durch: was der Block macht, warum er nötig ist, und was er bei der aktuell verwendeten Testaufnahme tatsächlich liefert.
Die hier umgesetzte Kette weicht bewusst vom einfachsten theoretischen Ansatz ab: Statt eines fest eingestellten Referenzträgers zur Trägerauskopplung übernimmt eine Regelschleife (Costas Loop) das laufende Nachführen des Trägers — nötig, weil die Trägerablage bei realen Web-Empfänger-Aufnahmen weder exakt bekannt noch konstant ist (siehe Abschnitt „Von der Theorie zur Praxis" in Stufe 3).
Die verwendete Aufnahme
Grundlage ist eine 75 Sekunden lange IQ-Aufnahme eines Internet-Empfängers (Web-SDR), abgestimmt auf 77,50 kHz. „IQ" bedeutet: Es wurden nicht nur Lautstärke und Frequenz aufgezeichnet, sondern das vollständige Signal inklusive Phaseninformation — nur so lässt sich die PZF später auswerten. Die Aufnahme liegt als Stereo-WAV-Datei vor (linker Kanal = I, rechter Kanal = Q), mit 12.000 Abtastwerten pro Sekunde.
Wie die Auswertung aufgebaut ist
1. Kanäle zusammenführen
Die WAV-Datei liefert zwei getrennte Zahlenreihen (I und Q). Ein Block namens Float To Complex fügt beide zu einer einzigen Größe zusammen, die sowohl Amplitude als auch Phase des Signals trägt. Ohne diesen Schritt gäbe es später gar keine Phaseninformation, die man auswerten könnte.
2. Auf den Träger einstimmen, filtern, Datenrate senken
Ein Frequency Xlating FIR Filter erledigt drei Dinge gleichzeitig:
- Er schiebt den Träger exakt auf 0 Hz (falls er wie hier leicht daneben liegt).
- Er filtert alles außerhalb eines schmalen Bereichs um den Träger heraus (ein Tiefpassfilter, hier ±1000 Hz).
- Er reduziert die Abtastrate von 12.000 auf 6.000 Werte pro Sekunde (eine sogenannte Dezimation) — das spart Rechenleistung, ohne Information zu verlieren, weil im schmalen Frequenzband ohnehin nicht mehr drinsteckt.
Wichtig dabei: Das Filter darf nicht zu schmal sein. Die PZF-Modulation braucht selbst ein gewisses Frequenzband um den Träger herum (mehrere hundert Hz) — ein zu schmales Filter würde zwar den Träger sauberer erscheinen lassen, aber genau die Information wegfiltern, die ausgewertet werden soll. Getestet wurden Filterbreiten von 20 Hz bis 1000 Hz; ab etwa 200 Hz ist die volle Information vorhanden, mehr bringt keinen messbaren Vorteil mehr. Im Flowgraph sind 1000 Hz eingestellt, mit Sicherheitsabstand.
3. Lautstärke automatisch angleichen (AGC)
Eine AGC (Automatic Gain Control, wie man sie aus jedem Empfänger kennt) sorgt dafür, dass das Signal immer auf denselben Pegel normiert ist, bevor es in den nächsten Schritt geht. Das ist nötig, weil der folgende Schritt (der Costas Loop) nur bei einer bekannten, gleichbleibenden Signalstärke zuverlässig funktioniert.
4. Träger "einrasten" (Costas Loop)
Der Empfänger hat den Träger nicht exakt auf 77,5 kHz getroffen, und die Frequenz driftet leicht. Ein Costas Loop — im Prinzip ähnlich einer PLL (Phase-Locked Loop), wie sie auch in jedem Synthesizer eines Transceivers steckt — rastet auf den Träger ein und gleicht diese Abweichung laufend aus. Übrig bleibt danach nur noch die kleine, aufmodulierte Phasenabweichung durch die PZF-Chips, um die es eigentlich geht.
Wie „scharf" diese Regelschleife eingestellt ist (der Parameter w), wurde an der Aufnahme durchgetestet: Werte zwischen 0,0005 und 0,1 wurden verglichen, der beste gemessene Wert liegt bei w = 0,02 und ist im Flowgraph eingestellt.
5. Feine Phasenabweichung herausrechnen
Hier lag früher eine Fehlerquelle, die es wert ist, kurz erklärt zu werden: Der naheliegende Weg, die Phase aus dem Signal zu berechnen, liefert einen Winkel zwischen −180° und +180°. Der Costas Loop kann aber (mit etwa 50 % Wahrscheinlichkeit, technisch bedingt) genau auf den Randbereich dieses Winkelbereichs einrasten — dann würde die Berechnung ständig zwischen +179° und −179° hin- und herspringen, obwohl sich am Signal kaum etwas ändert. Jede einfache Filterung danach würde diese Sprünge fälschlich als starkes Signal werten und alles überdecken.
Die im Flowgraph verwendete Methode umgeht dieses Problem: Sie berechnet die Phasenabweichung auf einem anderen mathematischen Weg (über den Imaginärteil des Signals), der diesen Randbereich gar nicht erst durchläuft. Für die hier interessierenden kleinen Winkel (wenige Grad) liefert das praktisch dasselbe Ergebnis, aber ohne die Sprünge.
6. Mit der bekannten PZF-Folge vergleichen (Matched Filter)
Die PZF ist eine feste, im Voraus bekannte Bitfolge (512 „Chips", vergleichbar mit 512 Bits, die aber keine Nutzdaten sind, sondern ein festes Erkennungsmuster). Ein Matched Filter vergleicht das laufend eintreffende Signal fortlaufend mit dieser bekannten Folge — wie eine Schablone, die über das Signal geschoben wird. Passen beide gerade zusammen, entsteht ein deutlicher Ausschlag (Peak); ansonsten bleibt der Ausgang niedrig.
Was die Auswertung aktuell leistet
Am Ende der Kette steht ein Zahlenwert, der einmal pro Sekunde deutlich höher ausschlägt als dazwischen — der Korrelationspeak. Zur Einordnung der Zahlen: Ein Wert von 1,0 wäre ein theoretisch perfekter Treffer; erreicht wird das in der Praxis wegen Rauschen, Empfangsbedingungen und der hier gewählten Skalierung nicht.
Gemessen an der aktuellen Aufnahme:
- Peak-Höhe: rund 0,038, gegenüber einem „Rauschteppich" von 0,006 (± 0,005) zwischen den Peaks.
- Abstand Peak zu Rauschen: knapp 7 Standardabweichungen (σ) — ein Maß dafür, wie deutlich sich der Peak vom Rauschen abhebt. Als Faustregel: Bei mehr als etwa 5σ ist ein Zufallstreffer praktisch ausgeschlossen; 7σ ist ein sehr sicherer, zuverlässig erkennbarer Wert.
- Zeitliche Regelmäßigkeit: Die stärksten Peaks liegen fast exakt im Abstand von einer Sekunde auseinander, wie es die PZF-Aussendung vorgibt.
Dass die absolute Zahl (0,038) klein aussieht, hat einen einfachen Grund: Die zum Vergleich genutzte Schablone hat eine Amplitude von ±1, das tatsächliche Signal (die Phasenabweichung in Bogenmaß) aber nur eine Amplitude von grob ±0,1 bis ±0,2 — dieser Größenunterschied allein erklärt einen Großteil der Differenz. Für die Zuverlässigkeit der Erkennung zählt allein der Abstand zum Rauschen (die knapp 7σ), nicht die absolute Höhe.
Bei jedem Peak eine Meldung
Damit man diese Erkennung nicht nur im Diagramm ablesen muss, enthält der Flowgraph einen zusätzlichen Baustein, der bei jedem erkannten Peak automatisch eine Nachricht (in GNU Radio als „Message" oder PDU bezeichnet — ein einzelnes Datenpaket, unabhängig vom fortlaufenden Signalstrom) verschickt. Diese Nachricht enthält den Zeitpunkt (als Sample-Nummer) und die Höhe des jeweiligen Peaks.
Umgesetzt ist das über einen selbst geschriebenen Baustein (einen sogenannten Embedded Python Block, also ein kleines Stück Python-Code direkt im Flowgraph), der zwei Schwellwerte verwendet: Steigt der Wert über eine obere Schwelle, beginnt die Beobachtung; sobald er wieder unter eine untere Schwelle fällt, gilt der höchste dabei gemessene Wert als der Peak, und die Nachricht wird verschickt. Diese doppelte Schwelle (Hysterese) verhindert, dass ein leicht schwankendes Signal mehrfach als „Peak" gezählt wird. Zusätzlich sperrt der Baustein sich für kurze Zeit nach jeder Meldung (hier: 5000 Abtastwerte, also 0,83 Sekunden), damit nicht dieselbe Sekunde versehentlich doppelt zählt.
Die beiden Schwellwerte wurden anhand der gemessenen Werte gewählt: Die tatsächlichen Peaks liegen zwischen 0,029 und 0,038, einzelne Rauschspitzen reichen bis etwa 0,02 hoch. Die Schwellen liegen bei 0,026 (obere) und 0,016 (untere) — dazwischen, mit Abstand zu beiden Seiten. Angeschlossen ist ein Message Debug-Block, der jede eingehende Nachricht ausgibt. Am Beispiel der Aufnahme kommt exakt eine Meldung pro Sekunde, im Abstand von 1,000 Sekunde (mit einer Abweichung von unter einer hundertstel Sekunde) — passend zur einmal pro Sekunde gesendeten PZF.
Wie sich das Ergebnis nachprüfen lässt
Zum Flowgraph gehört ein separates Prüfprogramm (korrelation_check.py), das dieselbe Verarbeitungskette ohne grafische Oberfläche durchrechnet und drei Werte ausgibt: ob der Costas Loop eingerastet ist, wie deutlich der Korrelationspeak vom Rauschen absticht, und ob die Peaks tatsächlich im Sekundenabstand liegen. Das ist deutlich schneller als jedes Mal den Flowgraph mit Oberfläche zu starten, und liefert eine klare Ja/Nein-Aussage statt eines Diagramms, das man selbst interpretieren muss.
$ python3 korrelation_check.py SND.178.63.122.223_..._77.50_iq.wav
1. Costas-Lock: 0.00 Phasen-Wraps/s (LOCK)
2. Korrelation: Peak=0.038 Rauschen=0.006±0.005 SNR=6.8 Sigma (OK)
3. Sekundenraster: 15/15 der staerksten Peaks auf dem 1-s-Raster
$ python3 korrelation_check.py SND.178.63.122.223_..._77.50_iq.wav
1. Costas-Lock: 0.00 Phasen-Wraps/s (LOCK)
2. Korrelation: Peak=0.038 Rauschen=0.006±0.005 SNR=6.8 Sigma (OK)
3. Sekundenraster: 15/15 der staerksten Peaks auf dem 1-s-Raster
Offene Punkte
- Aus einem erkannten Peak eine echte Uhrzeit ableiten (welche Sekunde genau war das?) ist noch nicht umgesetzt — bisher wird nur der Peak selbst erkannt und gemeldet.
- Eine eigene Aufnahme direkt mit einem eigenen Empfänger (statt über einen Internet-Empfänger) würde eine bekannte, kalibrierbare Empfängerelektronik liefern und manche der hier beschriebenen Unsicherheiten vermeiden.
Download
.grc und generiertes .py), das Prüfprogramm und die verwendete IQ-Aufnahme als ZIP —
dcf77-pzf-praxis.zip herunterladen, entpacken, dann:
# Flowgraph mit grafischer Oberfläche:
gnuradio-companion phasentest.grc
# oder ohne Oberfläche pruefen:
python3 korrelation_check.py SND.178.63.122.223_2026-07-25T11_29_19Z_77.50_iq.wav
# Flowgraph mit grafischer Oberfläche:
gnuradio-companion phasentest.grc
# oder ohne Oberfläche pruefen:
python3 korrelation_check.py SND.178.63.122.223_2026-07-25T11_29_19Z_77.50_iq.wav
Enthält zusätzlich signalparameter.py zur ersten Sichtung eigener Aufnahmen. Eigene Aufnahmen von Web-Empfängern funktionieren direkt, sofern sie als Stereo-IQ-WAV mit 12 kHz Abtastrate vorliegen — center_frequency ist gegebenenfalls anzupassen (siehe Hinweis oben zur Trägerablage).
Stufe 3: Korrelative Phasenauswertung
Theorie: Matched Filter, Verarbeitungsgewinn, Latenzbilanz und prädiktive Taktdisziplinierung.
Stufe 2: Demodulation in GNU Radio
Die AM-Verarbeitungskette als echtzeitfähige Blockverschaltung im GNU Radio Companion.
Stufe 1: Signal demodulieren
Vom rohen WAV-Mitschnitt zur lesbaren Uhrzeit über Hilbert-Transformation und Hüllkurve.