CW-Demodulator-Erweiterungen

Adaptive Geschwindigkeitsschätzung, ein entschärftes Anlaufverhalten und Frequenznachführung — die drei in Stufe 2 offen gebliebenen Punkte, umgesetzt und gegen synthetische Testsignale sowie eine echte KiwiSDR-Aufnahme verifiziert.

Stufe 2 endete mit drei benannten, aber offenen Punkten: kein automatisches Geschwindigkeits-Tracking, ein Anlauf-Artefakt beim Rauschflur-Schätzer, keine Frequenznachführung. Dieser Artikel arbeitet alle drei ab — mit derselben Vorgabe wie zuvor: jede Änderung wird an einem eigenen, generischen Testsignal mit echten Messwerten verifiziert.

Architekturänderung: Freq Xlating FIR Filter statt Bandpass + Hilbert

Bevor es an die drei Punkte geht, eine strukturelle Änderung: Der Band Pass Filter + Hilbert-Block aus Stufe 2 wird durch einen einzigen Freq Xlating FIR Filter ersetzt, der pitch direkt nach Basisband mischt und filtert. Der Grund ist die Frequenznachführung (Punkt 3) — dieser Blocktyp akzeptiert eine neue Mittenfrequenz zur Laufzeit über einen Nachrichtenport, was einen geschlossenen Regelkreis erst ermöglicht. Nebenbei vereinfacht das die Kette um einen Block.

Blockschaltbild Stufe 3: Freq Xlating FIR Filter mit AFC-Rückkopplung, adaptiver Element Tagger

Der Nachrichtenport freq ist im GRC-Blockkatalog nur knapp dokumentiert. Das erwartete Nachrichtenformat (ein PMT-Paar (intern("freq"), double)) steht nicht im GRC-YAML, sondern nur im C++-Header-Kommentar des Blocks — ohne den Blick in den Quellcode wäre die Kopplung nicht auf Anhieb funktionsfähig gewesen.

1. Geschwindigkeitsschätzung: drei Kandidaten, eine bewusste Wahl

Warum: Stufe 2 nutzte einen festen wpm-Parameter; bei 35 WPM führte das zu einem Zeichenfehler, weil die für 20 WPM kalibrierten Klassifikationsschwellen nicht mitskalierten.

Die drei Kandidaten

  1. Exponentiell geglätteter Mittelwert (IIR) auf zwei unabhängigen Referenzwerten t_dit/t_dah, jeweils bei jeder neuen Messung leicht nachgeführt: $t_{\text{dit}} \leftarrow \alpha \cdot t_{\text{neu}} + (1-\alpha) \cdot t_{\text{dit}}$. Einfach, in der Praxis verbreitet.
  2. Sliding Window / Median über die letzten N erkannten Element-Dauern, sortiert; unteres Drittel definiert die Punkt-, oberes Drittel die Strich-Referenz. Robust gegen einzelne Ausreißer (Störimpulse), weil ein Median sie schlicht ignoriert statt sie anteilig einzurechnen.
  3. Regelkreis mit Phasendetektor und Loop-Filter — dieselbe Grundstruktur wie eine Phase-Locked Loop, nur auf Lauflängen statt auf eine Trägerphase angewendet: Eine gemessene Mark-/Pausendauer wird mit dem nächstliegenden ganzzahligen Vielfachen einer laufend geschätzten Grundperiode verglichen, die Abweichung („Phasenfehler") fließt gedämpft in die Korrektur der Periode ein.

Alle drei sind in der Praxis brauchbar. Ansatz 2 ist die robusteste Wahl gegenüber impulsivem Rauschen (echtes QRN/Krachen statt des hier verwendeten Gauß'schen Rauschens), weil ein Median Ausreißer schlicht ignoriert statt sie gedämpft einzurechnen. Umgesetzt wird Ansatz 3, weil er als einziger die physikalische Randbedingung des Signals — ein Strich ist exakt das 3-fache eines Punktes, eine Wortpause exakt das 7/3-fache einer Zeichenpause — direkt ins Modell einbaut, statt sie implizit über zwei getrennt gelernte Werte anzunähern. Das ist ein Freiheitsgrad weniger und strukturell näher an der Synchronisationstheorie, wie sie in der digitalen Signalverarbeitung für Taktrückgewinnung verwendet wird: Phasendetektor → Loop-Filter → gesteuerter Referenztakt.

Regelkreis im Detail

Für jede Mark-Dauer $d$ wird das nächstliegende ganzzahlige Vielfache $n \in \{1,3\}$ der aktuell geschätzten Periode $T$ bestimmt (1 = Punkt, 3 = Strich; die Entscheidungsgrenze liegt bei $2T$, dem Mittelpunkt). Die Abweichung $e = d - nT$ ist der Phasenfehler; er fließt gedämpft in eine Korrektur von $T$ ein. Pausen laufen in einem komplett separaten zweiten Regelkreis mit eigener Periode $T_{\text{gap}}$ und $n \in \{1,3,7\}$.

Filter und Hysterese verlängern jede gemessene Dauer um einen ungefähr konstanten Betrag, unabhängig von $n$ (gemessen: ein echter 60-ms-Punkt bei 20 WPM kommt nach der Kette bei ≈85–90 Samples an, ein 180-ms-Strich bei ≈205–225 — der Offset ist für $n=1$ und $n=3$ ähnlich groß, nicht proportional zu $n$). Ein reines Ein-Parameter-Modell „Dauer $\approx n \cdot T$" kann Punkt und Strich unter diesem Bias nicht gleichzeitig exakt treffen — ein $T$, das zum Punkt passt, unterschätzt systematisch den Strich. Das Modell nutzt deshalb zwei Parameter, $\text{Dauer} \approx n \cdot T + b$, wobei $b$ den festen Verarbeitungs-Bias auffängt und $T$ die reine Taktrate. Beide werden gemeinsam per Normalized LMS (derselbe adaptive Standardfilter wie in einem Kanalentzerrer, Regressor $x=[n,1]$, Gewichte $[T,b]$) aus jeder neuen Messung aktualisiert:

$$\hat{d} = nT + b, \quad e = d - \hat{d}, \quad [T, b] \mathrel{+}= \mu \cdot \frac{e}{n^2+1} \cdot [n, 1]$$

Das ist dieselbe Grundidee wie die Trennung von Taktfehler und fester Signallaufzeit in der klassischen Synchronisationstheorie — nur auf Lauflängen statt auf eine Trägerphase angewendet.

Farnsworth-Kompatibilität (unverändert wichtiges Kriterium, siehe Stufe 1): Die beiden Regelkreise sind komplett unabhängig — der Pausen-Regelkreis beeinflusst $T_{\text{mark}}/b_{\text{mark}}$ nie, und umgekehrt. Die einzige bewusste Kopplung: Die untere Pausen-Klassifikationsgrenze (Element-Innenabstand vs. Zeichenpause) nutzt die vorhergesagte Mark-Periode $T_{\text{mark}}+b_{\text{mark}}$ als Anker, weil der Innenabstand innerhalb eines Zeichens auch bei Farnsworth-Timing nicht gestreckt wird.

Prüfweg

Test Ergebnis
20 WPM, korrekte Startschätzung „CQ CQ DE DL1ABC TEST" — korrekt (Stufe-2-Regression besteht)
35 WPM, korrekte Startschätzung „CQ CQ DE DL1ABC TEST" — korrekt, der Stufe-2-Fehler (S→N) ist behoben
20 WPM, 3 dB SNR „VVV DE DL1ABC K" — korrekt
15→30 WPM Geschwindigkeitsrampe „CQ CQ DE DL1ABC TEST" — korrekt
30 WPM, Startschätzung 20 WPM (33 % Fehler) „CQ CQ DE DL1ABC TEST" — korrekt
30 WPM, Startschätzung 15 WPM (Faktor 2) „??H??" — Fehlschlag

Geschwindigkeitsrampe: T_mark folgt der Rampe, b_mark bleibt stabil

Die Grafik zeigt, dass die Trennung von Rate und Bias wie vorgesehen funktioniert: Das WPM-Äquivalent von $T_{\text{mark}}$ folgt der realen 15→30-WPM-Rampe (mit Regelkreis-typischem Nachlauf, ähnlich der AFC unten), während $b_{\text{mark}}$ über den größten Teil der Aufnahme nahezu konstant bleibt — der Verarbeitungs-Bias ändert sich nicht mit der Geschwindigkeit, wird also korrekt getrennt von der eigentlichen Rate gehalten.

Zum Beitrag des Bias-Terms: Ein Modell ohne $b$ (reines $d \approx nT$) liefert bei den sauberen Testsignalen (20/35 WPM, Drift, Rampe) identische, korrekte Ergebnisse. Beim verrauschten Testsignal (3 dB SNR) klassifiziert es dagegen ein Zeichen falsch („E?VV" statt „EVVV"), während das Modell mit Bias-Term korrekt decodiert. Der zusätzliche Parameter zahlt sich also konkret im verrauschten Fall aus, nicht in den sauberen Fällen.

Bekannte Grenze: Eine stark falsche, aber konstante Anfangsschätzung (Faktor ≥2, z. B. 15 statt tatsächlich 30 WPM) bleibt ein Fehlschlag: Echte Striche werden anfangs als Punkte klassifiziert, was $T$ in die falsche statt die richtige Richtung zieht.

2. Anlauf-Artefakt entschärft

Warum: In Stufe 2 erzeugte die Warmlaufzeit des Rauschflur-Schätzers (bis die Bin-Historie einmal komplett durchlaufen ist) zuverlässig ein falsches Zeichen am Anfang jeder Aufnahme.

Umgesetzte Lösung: Der Block zählt jetzt mit, wie oft die Bin-Historie bereits aktualisiert wurde (_bins_filled). Solange das keine 20 vollen Zyklen sind, gibt er eine Ratio von 0 aus (statt eines künstlich überhöhten Wertes) — der nachfolgende Threshold-Block bleibt dadurch stabil auf „Space", bis der Rauschflur eingeschwungen ist.

Zweite, unabhängige Änderung: Die Fenstergrößen für Glättung/Bin-Historie hängen nicht mehr vom (ggf. falschen oder sich ändernden) wpm-Parameter ab, sondern von einer festen ref_wpm=30-Referenz. Grund: Ein zu großzügig bemessenes Fenster (abgeleitet aus einer zu niedrigen Geschwindigkeitsannahme) verschmiert schnelles CW komplett unbrauchbar, während ein zu kurzes Fenster bei langsamem CW nur etwas weniger Rauschunterdrückung bedeutet — die Asymmetrie macht eine feste, großzügig schnelle Referenz zur sichereren Wahl.

Prüfweg: Stufe 2 lieferte konsistent ein zusätzliches Zeichen am Anfang (z. B. „T" bei „TCQ CQ DE DL1ABC TEST"). Nach der Änderung ist es entweder ganz verschwunden oder auf ein einzelnes Leerzeichen reduziert (z. B. „ CQ CQ DE DL1ABC TEST" — ein Leerraum statt eines Buchstabens), je nach genauem Timing der Test-Signal-Vorlaufzeit relativ zu den Bin-Grenzen. Das verbleibende Leerzeichen ist unschädlich: In der Praxis läuft ein Empfänger durch, dieses Artefakt tritt dann nur einmal beim Programmstart auf, nicht vor jeder Nachricht.

3. AFC — Frequenznachführung

Warum: CW-Signale driften (Sender-Frequenzfehler, Doppler) — Anforderung aus Stufe 1, Abschnitt 6. Ein fest zentrierter Filter verliert den Ton, sobald die Drift über die Filterbandbreite hinausgeht.

Funktionsprinzip: siehe Frequenzregelung (AFC) für das allgemeine Prinzip. Konkret: Der neue CW AFC-Block schätzt die Momentanfrequenz des komplexen Basisbandsignals während erkannter Marks über die Phasendifferenz aufeinanderfolgender Samples. Bei perfekt zentriertem Ton ist diese Momentanfrequenz 0 Hz; jede Restfrequenz wird gedämpft aufintegriert und der Mittenfrequenz des Freq Xlating FIR Filter per Nachricht aufaddiert.

Zwei Details der Umsetzung sind für die Korrektheit der Schätzung entscheidend:

  1. Ausschluss der Flankenbereiche: Die geformten Ein-/Austastflanken (5-ms-Raised-Cosine, siehe Stufe 1, Abschnitt 3) erzeugen während der Anstiegs-/Abfallzeit kurzzeitig sehr große, aber bedeutungslose Momentanfrequenz-Ausschläge (bis über 100 Hz, gemessen) — die Amplitudenrampe selbst erscheint der Phasendifferenz-Schätzung als „Frequenz". Die ersten und letzten guard_samples jedes Marks werden deshalb von der Mittelung ausgeschlossen.
  2. Eigenständiges Gate statt fremdem Zweig: Das Gate für die Frequenzschätzung wird direkt aus der Hüllkurve desselben komplexen Signals gebildet, dessen Phase ausgewertet wird — nicht aus dem Mark/Space-Signal des Rauschflur-/Threshold-Zweigs. Grund: Der Rauschflur-Block hat durch seinen gleitenden Mittelwert eine eigene, gegenüber dem direkten Zweig nicht ausgeglichene Verzögerung von mehreren zehn Samples; ein daraus abgeleitetes Gate würde die Phasendifferenz an leicht falschen Sample-Positionen auswerten und die Schätzung mit falschem Vorzeichen verfälschen. Der AFC-Block bildet sein Gate deshalb selbst, über einen eigenen, unverzögerten Spitzenwert-Tracker auf |cx|.

Dimensionierung: loop_gain=0.05, empirisch gegen das Drift-Testsignal ermittelt (0,1 überschwingt bereits deutlich, 0,02 hinkt zu weit hinterher). guard_samples=15 (Samples am Anfang/Ende jedes Marks, die von der Mittelung ausgeschlossen werden).

Prüfweg: Testsignal mit linearer Drift von 700 Hz auf 760 Hz über die gesamte Aufnahme (20 WPM). Decodiertes Ergebnis: „CQ CQ DE DL1ABC TEST" — korrekt, trotz 60 Hz Drift bei einer Nutzbandbreite von nur 100 Hz. Die tatsächlich nachgeführte Korrektur bleibt dabei sichtbar hinter dem realen Drift zurück (Regelkreis mit Update nur einmal pro abgeschlossenem Mark, kein PI-Regler):

AFC-Korrektur gegen echten linearen Frequenzdrift

Am Ende der Aufnahme hat die Korrektur ≈50 Hz von 60 Hz realem Drift aufgeholt — der Rest liegt innerhalb der verbleibenden Akquisitionsbandbreite und stört die Decodierung hier nicht, wäre bei stärkerer oder schnellerer Drift aber ein Limit.

Gesamtergebnis

Testsignal Stufe 2 Stufe 3
20 WPM korrekt (+1 Anlauf-Zeichen) korrekt (Anlauf-Zeichen → Leerzeichen)
35 WPM 1 Zeichenfehler (S→N) korrekt
20 WPM, 3 dB SNR korrekt (+1 Anlauf-Zeichen) korrekt
20 WPM, 60 Hz Drift nicht getestet (kein AFC) korrekt
30 WPM, falsche Startschätzung (≤33 % Fehler) n/a korrekt
30 WPM, falsche Startschätzung (Faktor 2) n/a Fehlschlag (dokumentierte Grenze)
15→30 WPM Geschwindigkeitsrampe n/a korrekt

4. Verifikation an einer echten Aufnahme

Alle bisherigen Tests liefen gegen synthetische Signale. Für einen Praxisabgleich dient eine echte, per KiwiSDR im 40-m-Band aufgezeichnete IQ-Datei (12 kHz Abtastrate, Mittenfrequenz 7033,98 kHz, 36 s Dauer).

Architekturanpassung für echtes IQ

Eine KiwiSDR-IQ-Aufnahme unterscheidet sich strukturell von den bisherigen Testsignalen: Sie ist eine stereophone WAV-Datei (Kanal 0 = I, Kanal 1 = Q) mit dem vollen HF-Basisband um die Empfangsfrequenz — nicht ein einzelner Audiokanal mit einem Ton im BFO-Bereich. Der neue Flowgraph cw_live.grc liest beide Kanäle separat ein und kombiniert sie per Float To Complex zu einem echten komplexen IQ-Strom; das nachfolgende Freq Xlating FIR Filter läuft dadurch im Modus „Complex → Complex" (ccf) statt „Float → Complex" (fcf) wie in cw.grc. Rauschflur-Normalizer, Threshold, Element Tagger, Decoder und AFC sind unverändert dieselben epy_block-Module wie zuvor — nur das Front-End ändert sich. decim=12 bei 12 kHz Eingangsrate ergibt dieselbe working_rate=1000 Sa/s wie in cw.grc, sodass alle bisher empirisch ermittelten Werte (Threshold-Schwellen, mu, ref_wpm) unverändert gültig bleiben.

Das Zielsignal finden

Anders als bei den Testsignalen ist weder Tonhöhe noch Geschwindigkeit bekannt. Eine FFT über die gesamte Aufnahme zeigt mehrere Spektrallinien; ein Vergleich der Element-Dauern-Verteilung an jeder Linie (kurze, konsistent gruppierte Marks als Hinweis auf echte Zeichentastung) identifiziert eine bei +4511 Hz Versatz als plausibles CW-Signal — mit durchgehenden Auf/Ab-Perioden und einer klaren Pause zwischen 17 s und 23 s, die zu einer Wort- oder Nachrichtengrenze passt:

Spektrogramm der KiwiSDR-Aufnahme mit markiertem CW-Signal

Mit pitch=4511 Hz und einer groben Geschwindigkeitsschätzung von 18 WPM liefert die Kette: 'W TT OT ? ?' — kein sinnvoller Text.

Diagnose: zwei Signale statt eines

Eine hochauflösende Spektralanalyse genau um 4511 Hz löst die vermeintlich einzelne Linie in zwei getrennte Signale auf, nur rund 11 Hz auseinander, beide durchgehend über die gesamte Empfangsdauer aktiv:

Hochaufgelöstes Spektrum zeigt zwei Linien, die resultierende Hüllkurve zeigt eine Schwebung

Zwei Signale, die innerhalb der Filterbandbreite liegen, erzeugen eine Schwebung — die Hüllkurve pendelt kontinuierlich mit der Differenzfrequenz (~11 Hz) zwischen den Amplituden beider Träger, statt sauber zwischen „Ton" und „Stille" zu wechseln. Der Rauschflur-Normalizer und der Threshold-Block, die beide auf einen klaren Kontrast zwischen Mark und Space angewiesen sind, werten diese Schwebung als eine schnelle Folge kurzer, unregelmäßiger Marks — genau das Muster, das der Decoder als Zufallszeichen ausgibt.

Dieses Verhalten ist eine direkte Konsequenz der Architektur: Sowohl cw.grc als auch cw_live.grc sind auf ein isoliertes Signal in der gewählten Filterbandbreite ausgelegt, wie es die synthetischen Testsignale (und auch das AFC-Design mit seinem eigenständigen Hüllkurven-Gate) voraussetzen. Die reale 40-m-Aufnahme verletzt diese Annahme schon bei der stärksten verfügbaren Linie — im belegten CW-Segment eines Amateurfunkbands stehen Signale oft nur wenige Hertz auseinander. Zwei nahe beieinanderliegende Stationen sauber zu trennen ist ein eigenständiges Problem (schmalere Filterung mit entsprechend längerer Einschwingzeit, oder ein Mehrsignal-Ansatz wie die bandweite Bayessche Detektion von CW Skimmer, siehe Stufe 2) und nicht Teil dieser Verarbeitungskette.

Gesamtergebnis

Testsignal Stufe 2 Stufe 3
20 WPM korrekt (+1 Anlauf-Zeichen) korrekt (Anlauf-Zeichen → Leerzeichen)
35 WPM 1 Zeichenfehler (S→N) korrekt
20 WPM, 3 dB SNR korrekt (+1 Anlauf-Zeichen) korrekt
20 WPM, 60 Hz Drift nicht getestet (kein AFC) korrekt
30 WPM, falsche Startschätzung (≤33 % Fehler) n/a korrekt
30 WPM, falsche Startschätzung (Faktor 2) n/a Fehlschlag (dokumentierte Grenze)
15→30 WPM Geschwindigkeitsrampe n/a korrekt
Echte 40-m-KiwiSDR-Aufnahme (zwei benachbarte Signale) nicht getestet Fehlschlag (dokumentierte Grenze)

Verbleibende Grenzen

  • Stark falsche Anfangsschätzung (Faktor ≥2) lässt den Regelkreis in die falsche Richtung laufen — echte Striche werden anfangs als Punkte fehlinterpretiert. Ein Bootstrap-Kaltstart (Minimum der ersten paar Mark-Dauern als Startwert) wäre robuster gegenüber einer falschen Anfangsschätzung, verschluckt dabei aber die ersten Zeichen komplett, während er kalibriert — für kurze Nachrichten ein schlechterer Kompromiss als eine feste, ungefähr richtige Startschätzung.
  • AFC ist ein reiner Integrator ohne Vorhersage — bei sehr schneller oder sehr großer Drift (deutlich über den hier getesteten 60 Hz) würde der Ton die Akquisitionsbandbreite verlassen, bevor die Korrektur nachgezogen hat.
  • Impulsives Rauschen (echtes QRN) wurde nicht getestet — nur additives Gauß'sches Rauschen. Der Sliding-Window/Median-Kandidat (Ansatz 2 oben) wäre dagegen voraussichtlich robuster als der gewählte Regelkreis, der jede Messung anteilig verarbeitet statt Ausreißer zu verwerfen.
  • Kein Mehrsignal-Empfang: Wie die Live-Aufnahme zeigt, setzt die gesamte Kette ein einzelnes isoliertes Signal in der Filterbandbreite voraus. Dicht belegte Bänder mit mehreren Stationen im selben Filterdurchlass werden nicht getrennt.

Download

Download: Flowgraph (.grc), Build-Skript, Testsignal-Generator und alle Testsignale als ZIP — cw-demodulator-erweiterungen.zip herunterladen, entpacken, dann:
grcc -o build cw.grc
python3 build/cw.py
# oder headless zur reinen Verifikation:
python3 test_chain.py
grcc -o build cw.grc
python3 build/cw.py
# oder headless zur reinen Verifikation:
python3 test_chain.py

Nach Änderungen an einer epy_*.py-Datei python3 build_grc.py ausführen, um cw.grc neu zu erzeugen (der eingebettete Python-Quelltext wird nicht von Hand im YAML gepflegt).