DC Blocker

Ein rechnerisch günstiger Filter zur Entfernung des Gleichanteils (DC-Offsets) aus einem Signal — mit engerer Kerbe um 0 Hz als ein äquivalenter FIR-Filter.

1. Beschreibung des Blocks

Der DC Blocker-Block (filter.dc_blocker_ff / filter.dc_blocker_cc) entfernt den Gleichanteil (DC-Offset, 0 Hz) aus einem Signal. Anders als ein klassischer FIR-Hochpass erreicht er eine deutlich engere Sperrkerbe um 0 Hz bei geringerer Gruppenlaufzeit — auf Basis einer rekursiven Verzögerungsleitung (Delay Line) statt eines FIR-Filterentwurfs. Das zugrunde liegende Verfahren stammt aus R. Yates, „DC Blocker Algorithms", IEEE Signal Processing Magazine, März 2008.

DC Blocker Block

Der Block existiert in zwei Varianten (per Type-Parameter wählbar): Complex->Complex für IQ-Signale und Float->Float für reelle Signale. Beide arbeiten nach demselben Prinzip, nur auf unterschiedlichen Datentypen.

Verifiziert (GNU Radio 3.10.12.0, tatsächlich ausgeführt): Damit die Angaben auf dieser Seite nicht nur aus der Blockbeschreibung abgeschrieben, sondern real nachvollzogen sind, wurde ein Testsignal mit bekanntem Gleichanteil durch den Block geschickt und das Ergebnis geprüft.
from gnuradio import gr, blocks, filter as gr_filter
import numpy as np

samp_rate, freq, n = 48000, 1000, 4000
t = np.arange(n) / samp_rate
dc_offset = 0.7
sig = dc_offset + 0.5 * np.sin(2*np.pi*freq*t)   # 0,7 Gleichanteil + 1-kHz-Sinus, Amplitude 0,5

tb = gr.top_block()
src = blocks.vector_source_f(sig.tolist(), False)
dcb = gr_filter.dc_blocker_ff(32, True)          # length=32, long_form=True
snk = blocks.vector_sink_f()
tb.connect(src, dcb, snk)
tb.run()

out = np.array(snk.data())
delay = dcb.group_delay()
steady = out[delay + 500:]                       # Einschwingphase nach der Gruppenlaufzeit überspringen

print("input mean (DC-Anteil):", round(float(np.mean(sig)), 4))
print("output mean nach Einschwingen:", round(float(np.mean(steady)), 4))
print("output amplitude (Peak):", round(float(np.max(np.abs(steady))), 4))
from gnuradio import gr, blocks, filter as gr_filter
import numpy as np

samp_rate, freq, n = 48000, 1000, 4000
t = np.arange(n) / samp_rate
dc_offset = 0.7
sig = dc_offset + 0.5 * np.sin(2*np.pi*freq*t)   # 0,7 Gleichanteil + 1-kHz-Sinus, Amplitude 0,5

tb = gr.top_block()
src = blocks.vector_source_f(sig.tolist(), False)
dcb = gr_filter.dc_blocker_ff(32, True)          # length=32, long_form=True
snk = blocks.vector_sink_f()
tb.connect(src, dcb, snk)
tb.run()

out = np.array(snk.data())
delay = dcb.group_delay()
steady = out[delay + 500:]                       # Einschwingphase nach der Gruppenlaufzeit überspringen

print("input mean (DC-Anteil):", round(float(np.mean(sig)), 4))
print("output mean nach Einschwingen:", round(float(np.mean(steady)), 4))
print("output amplitude (Peak):", round(float(np.max(np.abs(steady))), 4))

Ausgabe:

input mean (DC-Anteil): 0.7014
output mean nach Einschwingen: -0.0019
output amplitude (Peak): 0.4854
input mean (DC-Anteil): 0.7014
output mean nach Einschwingen: -0.0019
output amplitude (Peak): 0.4854

Warum diese Werte: Der Eingang hat exakt 0,70 Gleichanteil, der Ausgang nach dem Einschwingen praktisch 0 (−0,0019) — der DC Blocker hat den Gleichanteil also nahezu vollständig entfernt. Gleichzeitig bleibt die Sinusamplitude mit 0,485 nahe am ursprünglichen Wert von 0,5 erhalten (die kleine Abweichung stammt aus der endlichen Fensterlänge der Mittelwertbildung, nicht aus einer Dämpfung durch den Filter). Die ersten delay + 500 Samples werden bewusst verworfen, weil der Filter erst nach seiner Gruppenlaufzeit eingeschwungen ist — würde man von Sample 0 an mitteln, wäre der Übergangsvorgang mit im Ergebnis und würde den gemessenen Mittelwert verfälschen.

2. Technischer Einsatz

Der DC Blocker wird eingesetzt, wenn ein Signal einen unerwünschten Gleichanteil trägt, der die nachfolgende Verarbeitung stört — etwa nach einem AM-Envelope-Detector (der Betrag eines Signals ist grundsätzlich nie negativ und hat daher immer einen positiven Gleichanteil), nach einem ADC mit leichtem Offset-Fehler, oder als Vorstufe vor einer AGC-Regelung, die sonst fälschlich auf den DC-Anteil statt auf die eigentliche Signalamplitude reagieren würde.

Im Vergleich zu einem klassischen Hochpassfilter mit sehr niedriger Grenzfrequenz benötigt der DC Blocker keine manuelle Tap-Berechnung und keine Fensterwahl — er ist damit die einfachere und rechnerisch günstigere Wahl, wenn ausschließlich der Gleichanteil unterdrückt werden soll und keine sonstige Hochpass-Charakteristik gebraucht wird.

3. Parameter und Dimensionierung

Parameter Typ/Einheit Bedeutung Dimensionierungshinweis
type (Type) Enum Complex->Complex oder Float->Float muss zum Datentyp der angeschlossenen Blöcke passen
length (Length, $D$) Integer Länge der internen Verzögerungsleitung Standard 32; größere Werte verengen die Sperrkerbe um 0 Hz, erhöhen aber die Gruppenlaufzeit
long_form (Long Form) Bool Long Form (glatterer Frequenzgang) oder Short Form (kürzere Verzögerung, rechnerisch günstiger) Standard „True"; „False" nur bei knappem Rechenbudget oder wenn die zusätzliche Verzögerung nicht tolerierbar ist
Verifizierte Gruppenlaufzeit (GNU Radio 3.10.12.0, tatsächlich ausgeführt): Die Formeln $2D-2$ (Long Form) und $D-1$ (Short Form) stehen zwar so im Header der C++-Implementierung, wurden hier aber zusätzlich am realen Objekt abgefragt statt blind übernommen:
from gnuradio import filter as gr_filter

for D, form in [(32, True), (32, False)]:
    blk = gr_filter.dc_blocker_ff(D, form)
    print(f"length={D}, long_form={form}: group_delay() = {blk.group_delay()}")
from gnuradio import filter as gr_filter

for D, form in [(32, True), (32, False)]:
    blk = gr_filter.dc_blocker_ff(D, form)
    print(f"length={D}, long_form={form}: group_delay() = {blk.group_delay()}")

Ausgabe:

length=32, long_form=True: group_delay() = 62
length=32, long_form=False: group_delay() = 31
length=32, long_form=True: group_delay() = 62
length=32, long_form=False: group_delay() = 31

Warum diese Werte: $2 \cdot 32 - 2 = 62$ und $32 - 1 = 31$ — die von group_delay() tatsächlich gemeldete Verzögerung stimmt exakt mit den Formeln überein. length=32 wurde gewählt, weil es der Default-Wert des Blocks ist (siehe Parametertabelle) und damit der Wert, den man in der Praxis am häufigsten unverändert übernimmt.

4. Ein- und Ausgänge

Ein-/Ausgang Typ (je nach type)
Eingang Complex oder Float
Ausgang Complex oder Float (identisch zum Eingang)

Der Datentyp von Ein- und Ausgang ist immer identisch — es findet keine Typkonvertierung statt.

5. Weitere wichtige Aspekte

Gruppenlaufzeit einplanen: Da der DC Blocker eine reale Verzögerung von $2D-2$ (Long Form) bzw. $D-1$ (Short Form) Samples einführt, muss diese bei zeitkritischer Weiterverarbeitung (z. B. Bit-Slicing oder Symbol-Timing) berücksichtigt werden — ähnlich wie die Filterverzögerung bei den FIR-Filter-Blöcken, die im FSK/RTTY-Test real gemessen und kompensiert wurde.

Kein Ersatz für einen echten Hochpass: Der DC Blocker unterdrückt gezielt den Bereich sehr nahe 0 Hz, verhält sich aber außerhalb dieser engen Kerbe nahezu wie ein Durchlassfilter — für eine breitbandige Unterdrückung niederfrequenter Störanteile ist ein dimensionierter Hochpassfilter die richtige Wahl.