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).

Was ist PZF nochmal? DCF77 sendet die Uhrzeit normalerweise über Amplitudentastung (kurze Absenkungen der Sendeleistung, 100 oder 200 ms lang). Seit 1983 überträgt der Sender zusätzlich eine feste, bekannte Bitfolge als winzige Phasenverschiebung des Trägers (wenige Grad) — nicht als Lautstärkeänderung, sondern als Verschiebung der Schwingung selbst. Ein normaler DCF77-Wecker bemerkt davon nichts. Wer diese Phasenfolge auswertet, kann die Sekundenmarke deutlich genauer bestimmen als über die Amplitudentastung.

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.

Eine Falle beim Prüfen der Aufnahme: Schaut man sich das Spektrum einer solchen Aufnahme an, erscheint oft ein auffälliger Ausschlag exakt bei 0 Hz. Das kann der echte Träger sein — es kann aber auch ein technischer Fehler des Empfängers sein (ein sogenannter Gleichspannungsanteil, der bei vielen SDR-Empfängern durch die Elektronik entsteht und nichts mit dem Funksignal zu tun hat). Um das zu unterscheiden, filtert man diesen Gleichanteil versuchsweise heraus: Verschwindet der Ausschlag bei 0 Hz komplett, war er ein Elektronik-Artefakt. Bei der hier verwendeten Aufnahme liegt der echte Träger sehr nah an 0 Hz, was sich zusätzlich daran zeigt, dass sich das Ergebnis über einen Testbereich von −8 bis +8 Hz nicht ändert.

Wie die Auswertung aufgebaut ist

WAV-Datei (I- und Q-Kanal) │ ▼ Kanäle zu einem Signal zusammenführen │ ▼ Auf den Träger einstimmen, filtern, Datenrate senken │ ▼ Lautstärke automatisch angleichen (AGC) │ ▼ Träger "einrasten" (Costas Loop) │ ▼ Feine Phasenabweichung herausrechnen │ ▼ Mit der bekannten PZF-Folge vergleichen (Matched Filter) │ ▼ Peak erkennen → Meldung ausgeben

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.

Chipdauer bestätigt: Laut der offiziellen PTB-Seite zur DCF77-Phasenmodulation (Quelle: Piester, D. (2004): „Zeit- und Normalfrequenzverbreitung mit DCF77", PTB-Mitteilungen 114(4), S. 345) dauert die Phasenmodulation 512 Chips lang rund 793 ms je Sekunde — nicht die volle Sekunde. Das ergibt eine Chipdauer von 793 ms / 512 ≈ 1,55 ms, also rund 645,7 Chips pro Sekunde. Das deckt sich fast exakt mit der im Flowgraph verwendeten Annahme (120 Trägerschwingungen pro Chip ≈ 645,8 Chips/s, 792,7 ms für 512 Chips) — die frühere zweite Annahme (512 Chips/s über die volle Sekunde) ist damit widerlegt. Derselbe PTB-Quelle zufolge beträgt der Phasenhub ±15,6° (nicht die in älteren Versionen dieses Artikels grob geschätzten ±13°).

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.
Was bedeutet „Standardabweichung" hier konkret? Die Standardabweichung beschreibt, wie stark eine Reihe von Messwerten üblicherweise um ihren Mittelwert schwankt — kleine Standardabweichung heißt „die Werte liegen eng beieinander", große heißt „auch deutliche Ausreißer sind normal". Ermittelt wird sie hier, indem das Prüfprogramm über den kompletten Korrelations-Signalstrom (nach Abzug der Einschwingzeit) Mittelwert und Standardabweichung bildet; da die kurzen Peaks unter tausenden Werten pro Sekunde kaum ins Gewicht fallen, entspricht das Ergebnis in guter Näherung dem Rauschteppich. Der Abstand des Peaks vom Rauschen wird dann nicht in absoluten Zahlen gemessen, sondern in Vielfachen dieser Standardabweichung: (Peak − Mittelwert) / Standardabweichung = (0,038 − 0,006) / 0,005 ≈ 6,8. Das ist aussagekräftiger als die absolute Zahl, weil sich Rauschen meist wie eine Glockenkurve um seinen Mittelwert verteilt: Ein zufälliger Ausreißer von 5 Standardabweichungen kommt nur etwa einmal unter 3,5 Millionen Werten vor, bei 7 Standardabweichungen nur noch etwa einmal unter mehreren hundert Milliarden. Ein Peak bei 6,8σ ist damit praktisch sicher ein echtes Signal und kein Zufallstreffer — dieselbe „Sigma"-Konvention nutzt z. B. auch die Teilchenphysik, um die Sicherheit einer Entdeckung anzugeben. Ausführlicher, inklusive Umrechnung zu dB und der praktischen Umsetzung als Schwellwert-Block: Peak-Erkennung: Wie sicher ist ein Treffer?

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

Download: Flowgraph (.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).