Forum: Digitale Signalverarbeitung / DSP / Machine Learning Design eines Bandpasses


von Resonanzkatastrophe (Gast)


Lesenswert?

Hallo :)

Ich versuche grade einen Digitalen Bandpass in C zu implementieren. 
Leider klappt das nicht ganz so wie gewünscht.

Ich versuche folgende Schaltung nachzubauen:
1
 Uin     ____          Uout
2
 o------|____|---+----+--o
3
           R     |    |
4
                 L   _|_C
5
                 L   ___
6
                 L    |
7
                 |    |
8
                 +----+
9
                      |
10
                     _|_
11
                      -

Die Übertragungsfunktion für das System ist:

wobei meine Resonanzfrequenz
ist und meine Bandbreite
Damit habe ich eine Null bei
und 2 Pole bei

Jetzt will ich diesen Filter in die Z-Ebene bringen.

Dieser Seite [1] habe ich die Transformation
entnommen, wobei T die Periodendauer meiner Abtastung ist.

Damit bekomme ich für die Nullstelle
und für die erste Polstelle:
Die zweite Polstelle ist entsprechend komplex konjugiert.

Nun multipliziere ich den Nenner aus:

Jetzt erhalte ich für mein ganzes Polynom:

Das nurnoch kürzen:

Erstmal, ist das soweit richtig?

Als nächstes habe ich einen konkreten Fall gewählt, und zwar
und

und das ganze in C++ implementiert. Als Umsetzung der Filterstruktur
habe ich die Direktform 2 gewählt [2].

Hier der dazugehörige Code:
1
#define _USE_MATH_DEFINES
2
#include <math.h>
3
#include <iostream>
4
5
int main()
6
{
7
  double w0 = 100 * 2 * M_PI; // 100Hz
8
  double w_band = 2 * M_PI * 10; // 10Hz -3db
9
  double samplerate = 1000; // abtastrate in Hz
10
11
  double w_res_sq = w0 * w0;
12
  
13
  double pol_real = -w_band / 2; // realteil des pols
14
  double pol_img = sqrt(w_res_sq - w_band * w_band / 4); // imaginärteil des pols
15
  double exp_real = pol_real / samplerate; // mit T skaliert
16
  double exp_img = pol_img / samplerate;
17
  // e^(a+bi) = e^a * e^(bi) = e^a * (cos(b)+i*sin(b))
18
  double x = exp(exp_real) * cos(exp_img); // lage des pols in der z-ebene
19
  double y = exp(exp_real) * sin(exp_img);
20
21
  // zähler
22
  double b0 = 0;
23
  double b1 = 1;
24
  double b2 = -1;
25
  // nenner
26
  double a0 = 1;
27
  double a1 = -2 * x;
28
  double a2 = x * x + y * y;
29
30
  double val0 = 0, val1 = 0, val2 = 0;
31
  double time = 0;
32
  for (unsigned i = 0; i < 1000; ++i)
33
  {
34
    double newval = sin(time * w0);
35
    time += 1 / samplerate;
36
    val2 = val1;
37
    val1 = val0;
38
    val0 = newval + b1 * val1 + b2 * val2;
39
40
    double out = a0 * val0 + a1 * val1 + a2 * val2;
41
42
    std::cout << "in = " << newval << std::endl;
43
    std::cout << "out = " << out << std::endl;
44
  }
45
}

Die Idee war dass ich ein Sinussignal auf den Eingang gebe, welches 
exakt die Resonanzfrequenz des Filters hat. An sich sollte der Filter 
langsam anfangen zu schwingen und am Schluss identisch zum Signal 
aussehen. Leider nimmt der Filter aber werte größer als 1 an, was bei 
diesem Eingangssignal nicht möglich ist. Daher scheine ich wohl irgentwo 
ein Fehler zu haben :(

Die Filterkoeffizienten sind:
1
b0 = 0
2
b1 = 1
3
b2 = -1
4
a0 = 1
5
a1 = -1.5688869346931174
6
a2 = 0.93910136742429273

Grüße,
Resonanzkatastrophe

Quellen:
[1] 
http://www.dummies.com/how-to/content/working-across-domains-example-take-the-rc-lowpass.html
[2] 
http://de.wikipedia.org/wiki/Filter_mit_unendlicher_Impulsantwort#Direkt-Form_2_.28DF2.29

von abcd (Gast)


Lesenswert?

Ich habe deine Übertragungsfunktion nur mal kurz durch Matlab gejagt. 
Deine Filterkoeffizienten stimmen auf jeden Fall, der Frequenzgang hat 
auch die gewollte Spitze bei 100Hz und zeigt das von dir erwartete 
Verhalten.

Das Problem ist, dass du deinen Filter noch auf eine bestimmte 
Verstärkung normieren musst. Das wird in deinem Link [1] auch unter 
"Fine-tune the design for implementation" erklärt.

In deinem Fall wäre es wohl am Einfachsten sich die Verstärkung im 
kontinuierlichen Filter bei der Resonanzfrequenz und im diskreten Filter 
bei der Resonanzfrequenz anzuschauen und dann den Gainfactor 
entsprechend zu wählen, dass es passt.

von Resonanzkatastrophe (Gast)


Lesenswert?

Hallo abcd

Du hast Recht, der Verstärkungsfaktor fehlt tatsächlich. Aber auch nach 
einfügen selbigens hatte der Filter ein komisches Verhalten, die 
eigentliche Ursache war im Code. Die Filterkoeffizienten waren 
vertauscht.

vorher:
1
val2 = val1;
2
val1 = val0;
3
val0 = newval + b1 * val1 + b2 * val2;
4
5
double out = a0 * val0 + a1 * val1 + a2 * val2;

nachher:
1
val2 = val1;
2
val1 = val0;
3
val0 = newval - a1 * val1 - a2 * val2;
4
5
double out = (b0 * val0 + b1 * val1 + b2 * val2) / scale;

Zum einen waren nenner und zählerkoeffizienten falsch und zum anderen 
muss ich subtrahieren statt addieren. So funktioniert der Filter wie 
gewünscht, nur weiß ich leider nicht warum das sorum sein muss. Bei der 
Abbildung auf Wikipedia waren die koeffizienten schließlich anders rum.

Viele Grüße,
Resonanzkatastrophe

von Freddy (Gast)


Lesenswert?

Resonanzkatastrophe schrieb:
> Bei der
> Abbildung auf Wikipedia waren die koeffizienten schließlich anders rum

In der Literatur ist man sich uneins, ob b die Zähler- und a die 
Nenner-Koeffizienten oder andersrum sind.

Bei dem Wikipedia-Artikel haben sie bei den Gleichungen b im Zähler so 
wie du, aber mit den Bildern waren sie dann ein bisschen inkonsequent:

Ersteres erklärt dein Vertauschen der Koeffizienten, zweiteres das 
nötige Subtrahieren.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.