phasen-test — DCF77-PZF-Demodulation
Praktische Umsetzung der korrelativen Phasenauswertung (PZF) des DCF77-Signals,
dokumentiert auf der Homepage unter „Projekte → DCF77 → Stufe 4: PZF-Demodulation
in der Praxis".
Dateien
phasentest.grc / dcf77_3.py — Flowgraph: IQ-Aufnahme → Frequency Xlating
FIR Filter (1000/400 Hz, Dezimation 2 → 6 kHz) → Feed Forward AGC → Costas
Loop (Ordnung 2, w=0.02) → Complex To Imag (Phasenabweichung ohne
atan2-Wickelpunkt) → Korrelation gegen die zeitgespiegelte 512-Chip-PZF-
Referenz. Complex To Arg + Grad bleibt nur als Diagnoseanzeige erhalten.
Zusätzlich ein Embedded-Python-Block (epy_block_0, CA-CFAR-Peak-Detektor,
siehe /blocks/peak-detektor-block/) hinter der normierten Korrelation, der
pro erkanntem Peak eine PMT-Message (Wert + Sample-Index) auf Port peak
sendet — verbunden mit einem Message Debug-Block zur Anzeige. Schätzt den
Rauschpegel laufend aus einem Trainingsfenster (statt fester Schwellwerte)
und passt sich damit automatisch an wechselnden Rauschpegel an - Verfahren:
https://pysdr.org/content/detection.html (Cell-Averaging CFAR). Der
Schwellenfaktor alpha wird NICHT an einer Aufnahme kalibriert, sondern
aus einer Ziel-Falschalarmrate pfa hergeleitet, mit einer Formel fuer die
tatsaechliche Statistik dieser Architektur (Betrag eines reellen
Matched-Filter-Ausgangs = halbnormalverteilt, nicht exponentialverteilt
wie in der Radar-Literatur): alpha = sqrt(pi) * erfcinv(pfa) - siehe
/blocks/peak-detektor-block/ fuer die Herleitung. Architekturbedingt
allgemeingueltig, keine Kalibrierung an dieser Aufnahme noetig.
Parameter: num_guard=100, num_train=600, pfa=1e-4 (daraus
alpha=4.876), beobachtungsfenster_samples=4500 (0,75 s),
sperrzeit_samples=500. Die kompilierte Klasse zeigt
zuverlaessige Erkennung im Sekundenabstand mit Praezision unter einer
halben Millisekunde (nach kurzer Costas-Einschwingzeit) - offline und im
Echtzeitlauf, unveraendert korrekt auch nachdem der Signalpegel in der
Kette versehentlich verdoppelt wurde
(Skaleninvarianz von CFAR).
korrelation_check.py — headless Verifikation derselben Kette (kein GUI,
kein Throttle): Costas-Lock (Wraps/s auf geglätteter Phase), Korrelations-SNR,
Sekundenraster-Prüfung inkl. Taktdrift-Schätzung, Einschwing-Sprung-Erkennung.
signalparameter.py — erste Sichtung unbekannter WAV-Konserven.
SND.178.63.122.223_2026-07-25T11_29_19Z_77.50_iq.wav — 75 s Web-Empfänger-
Mitschnitt (77,5 kHz, 12 kHz Stereo-IQ), aktuelle Referenzaufnahme:
SNR 6,8 σ, 15/15 Peaks auf dem 1-s-Raster, Taktdrift ≈ −400 ppm, kein
Einschwing-Sprung, unempfindlich gegenüber center_frequency (−8…+8 Hz
getestet).
SND.178.63.122.223_2026-07-25T11_25_13Z_77.50_iq.wav — zweiter Mitschnitt
desselben Empfängers, 4 Minuten früher.
SND.df0twn.dnsuser.de_2026-07-25T08_27_05Z_77.50_iq.wav — ältere
Testaufnahme, zeigte einen Einschwing-Sprung nach ~19–20 s (siehe
Erkenntnis 5 unten); besteht die Prüfungen ab Sekunde 20 mit SNR 7,2 σ,
15/15 Raster.
dcf77_2_iq.wav — älteste Aufnahme, Trägerablage ≈ −5 Hz.
Wichtigste Dimensionierungs-Erkenntnisse
- BESTÄTIGT: PZF-Chiprate 77500/120 = 645,83 Chips/s (0,7927 s Sequenzdauer)
deckt sich mit der offiziellen PTB-Quelle (Piester 2004, PTB-Mitteilungen
114(4), S.345; https://www.ptb.de/cms/ptb/fachabteilungen/abt4/fb-44/
ag-442/verbreitung-der-gesetzlichen-zeit/dcf77/phasenmodulation.html):
512 Chips dauern dort laut Diagramm ≈793 ms, nicht die volle Sekunde -
passend zur hier verwendeten Annahme. Phasenhub laut derselben Quelle:
±15,6° (nicht die frueher grob geschaetzten ±13°).
- Das Vorfilter muss die Modulation durchlassen (Hauptkeule ±512-646 Hz,
je nach Chipraten-Hypothese) — ein trägerschmales Filter verbessert den
Costas-Lock und zerstört gleichzeitig die Korrelation.
- Costas-Bandbreite: Messreihe 0.0005–0.1 zeigt einen breiten funktionierenden
Bereich. Gewählt: w = 0.02 (bester gemessener Wert an der aktuellen
Referenzaufnahme, 6.8σ, 15/15 Raster).
- Phasenextraktion: NICHT Complex To Arg (Grad) + DC Blocker verwenden - ein
Costas Loop 2. Ordnung rastet mit 50% Wahrscheinlichkeit auf 180° statt 0°
ein, dann liegt complex_to_arg auf der atan2-Wickelgrenze und ein
nachfolgender linearer Filter (DC Blocker) erzeugt Klingelartefakte bis
±250°. Stattdessen Complex To Imag (Im(z) ~= theta[rad] fuer kleine Winkel)
direkt in den Matched Filter - kein atan2, kein Wickelpunkt.
- Manche Aufnahmen zeigen einen Einschwing-Sprung: Korrelation bleibt einige
Sekunden nach Aufnahmestart auffällig schwach und springt dann abrupt auf
volles Niveau - unabhängig von der Costas-Bandbreite, also kein Bug in der
Kette, sondern vermutlich AGC-/Rauschsperren-Einschwingen des jeweiligen
Web-Empfängers. korrelation_check.py erkennt und meldet das automatisch.
Die aktuelle Referenzaufnahme zeigt diesen Effekt nicht.
Ausführen
gnuradio-companion phasentest.grc
# oder headless:
python3 korrelation_check.py SND.178.63.122.223_2026-07-25T11_29_19Z_77.50_iq.wav
gnuradio-companion phasentest.grc
# oder headless:
python3 korrelation_check.py SND.178.63.122.223_2026-07-25T11_29_19Z_77.50_iq.wav