mikrocontroller.net

Forum: Digitale Signalverarbeitung / DSP IIR Überlauf


Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe ein IIR Filter. Dieser wurde in C umgesetzt. Es würde mich
interessieren, wie ich mein Programm von einem Sättigungs-Überlauf
schützen kann.
Bei mir werden double Werte aufaddiert. Und es stehen 16bit zur
Verfügung.
Ich habe mir es so vorgestellt, das im Falle eines Überlaufs, keine
Fehlermeldung ausgegeben wird, sonder das der Wert bei dem höchsten
bzw. niedrigsten Wert weiter fährt und nicht in den niedrigsten bzw.
höchsten Bereich überspringt.

Ich würde mich über jeden Vorschlag freuen.
Danke!


Alex

Autor: TheMason (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Alex,

einen Überlauf kannst du effektiv nur feststellen wenn du mit
floating-point arbeitest (oder aber du hast bei integer entsprechend
reserve-bits übriggelassen).
Ich habe es bei meinem Filter (software-synthesizre) so gemacht das ich
den überlauf ignoriere, da der signalweg komplett in floating-point
gerechnet wird und erst bei der umsetzung von fp nach integer habe ich
lediglich eine begrenzung des wertes auf -1 bzw. +1 gemacht und dann
umgerechnet.
bei reiner integer-arithmetik sollte man so vorgehen genug reserve
einzurechnen (z.b. 8bit bei 24 bit audio, da kommt man auf einen
schönen 32-bit-wert). nach dem aufaddieren sollte man den wert clippen
( auf deine audio-breite) und dann erst in den rückkoppelzweig
reinschieben.
macht man keine begrenzung steht u.u. müll bzw. ungültige werte in
deinen audiodaten und das bringt den rückkoppelzweig u.u. zum schwingen
(hab das bei einer implementierung eines iir filters in einem fpga
gehabt)

hoffe ich konnte dir damit helfen

gruß
rene

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe mir folgendes Überlegt:


#define isnan(x) ((x) !=(x))
...
...
if (isnan(sum))
{
if ( sum < 0)
sum=DBL_MAX;
if (sum > 0)
sum=DBL_MIN;
}

Was haltet Ihr von dieser Version??

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nix, das geht so nicht. Dein define ist ja immer false. Integer Overflow
entweder wie Rene sagt mit nem Extrabit oder so: Addition mit ner Zahl >
0 muß die Summe größer machen und umgekehrt. Falls nicht: Overflow, ist
aber aufwendig und langsam. Aber wieso überhaupt sowas machen : 16Bit
float macht doch keinen Overflow !? Das ganze ist ne Frage der Dynamik:
Dein Filter machte Verstärkung <256, also 8 Bit, bei 16Bit Rechnung
solltest du höchstens mit 8 Bit Werten (<256) reingehen. Falls du mit
größeren Werten reingehst kriegst Du mit Integerrechnung Overflow und
die 16Bit float-Rechnung skaliert Dir Genauigkeit weg, da kanns dann
auch schöne Effekte geben.

Cheers
Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Detlef,

Das ganze muss auch für andere Filter (mit höheren Verstärkung)
geeignet sein. So das man nur die Koeffizienten austauscht und sonst
nichts.

Habe ich es richtig Verstanden?
...
...
sum=a+b;
...
if(a>0 && b>0 && sum<0)
  sum=DBL_max;
elsif(a<0 && b<0 && sum>0)
  sum=DBL_min;
else
  sum=sum;

...
...

Wie würde die Version mit nem extra Bit aussehen?

Gruss,
Alex

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, was fürn Typ hat denn a,b und sum?

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
alle drei sind fon Typ double.
Gruss,

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

float und double Typen laufen nicht über, die sind durch
Mantisse/Exponent Darstellung skaliert. Wenn Du auf nen großes double
ein kleines aufaddierst, bleibt der Wert einfach gleich. Folgende
Routine läuft bei mir bis dr ~ 9*10^15.

double dr=1.0;
while(dr != (dr+1.0) ){
          dr *= 2.0;
          printf("%e \n",dr);
}
return 0;

double/float sind für digitale Filter, auch IIR, ok und bequem, float
Rechnung dauert halt immer länger.

IIR mit integer verlangt große Sorgfalt. Erstens muß man die
Rückkoppelkoeffizienten mit einer Potenz von 2 skalieren, also
beispielsweise ne Multiplikation mit 0.641 erstzen durch 10503/16384,
das bringt Fehler rein, die das Verhalten des Filters stark ändern
können. Zweitens kann die Integerrechnung an sich zum Schwingen des
Filters führen. Drittens maß man sehr genau auf Overflows und die
Dynamik achten, am bestens mit dem Extrabit: bei ner
Integer-Zweierkomplementdarstellung muß das oberste Bit und das
zweitoberste übereinstimmen, sonst wirds als Overflow gewertet. Man
verliert ein Bit Dynamik aber das Testen auf Overflow ist einfach.

Aber wenn man Zeit hat ist integer Mist, lieber double.

Cheers
Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

es hat sich bei mir was geändert, der Rückgabe Wert (diesen habe ich
zuvor als Sum bezeichnet) ist von Type Integer.

Hier ist main aktuelles Programm:

int    gefiltert=0;
double*   A_K = o_dig_filt->A_K;
double*    B_K = o_dig_filt->B_K;
double     z1 = o_dig_filt->gefilterte_W[wert].z1;
double     z2 = o_dig_filt->gefilterte_W[wert].z2;
double    help_a=0;

help_a=z1*A_K[1]+z2*A_K[2];
gefiltert=(int)((ungefiltert-help_a)*B_K[0]+z1*B_K[1]+z2*B_K[2]);
o_dig_filt->gefilterte_W[wert].z2=z1;
o_dig_filt->gefilterte_W[wert].z1=(ungefiltert-help_a);

return gefiltert;

Wie kann ich es am besten gegen Überlau absichern, auch wenn es zu
einem Überauf nicht kommen kann?

Gruss,
Alex

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bißchen ergänzen
double    help_b=0.0;

help_b=((ungefiltert-help_a)*B_K[0]+z1*B_K[1]+z2*B_K[2]);
if(help_b > MAXINT) help_b=MAXINT;
if(help_b < MININT) help_b=MININT;
gefiltert =(int)help_b;

>Wie kann ich es am besten gegen Überlau absichern, auch wenn es zu
>einem Überauf nicht kommen kann?

Double macht keinen Overflow, integer schon

Cheers
Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo  Detlef,

Danke für die schnelle Hilfe!!!!!

MfG,
Alex

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Detlef,

mir ist hier was Aufgefallen:

>bißchen ergänzen
>double    help_b=0.0;

>help_b=((ungefiltert-help_a)*B_K[0]+z1*B_K[1]+z2*B_K[2]);
>if(help_b > MAXINT) help_b=MAXINT;
>if(help_b < MININT) help_b=MININT;
>gefiltert =(int)help_b;

wenn help_b in den Überlauf geht, dann bekommt dieser Wert ein
negatives Vorzeichen. So würde die nächste Abfrage gar nicht stimmen.
Da help_b dann kleiner als MAXINT ist.

Ist das richtig, oder habe ich da was übersehen?

Gruss,
Alex

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Alex,

help_b kann nicht in Überlauf gehen, das isnn double.

> bekommt dieser Wert ein negatives Vorzeichen.
welcher Wert ?

Du rechnest komplett in double, da läuft wie gesagt nix über. Bevor Du
nach integer zurückwandelst, prüfts Du ab, ob der double in den integer
Zahlenbreich paßt und schneidest dementsprechend ab. MAXINT/MININT sind
maschinenabhängig, wie groß sind denn deine integers??!

Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Detlef,

INT_MAX ist bei mir 2147483647
INT_MIN ist (-2147483647-1)

Warum kann double nicht überlaufen? Double-Type besitz doch auch
Grenzen.

Gruss,
Alex

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Warum kann double nicht überlaufen? Double-Type besitz doch auch
>Grenzen

habe ich versucht in meinem post vom 24.1. zu erklären. Du hast 32 Bit
integers.

Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Detlef,

ich habe im Internet nachgeforscht, und habe hier zu dem Thema was
gefunden.

UNI-Bremen:
"Bei einem Überlauf wird Double.POSITIVE_INFINITY oder
Double.NEGATIVE_INFINITY"

Microsoft:
"Gleitkommaoperationen zeigen durch die Rückgabe von PositiveInfinity
einen Überlauf an."

Das würde bedeuten, das bei einem "Überlauf" von double, würde dem
help_b  +unendlich bzw. -unendlich zugewiesen.
Anschgließend vergleicht man mit "if(help_b > MAXINT)
help_b=MAXINT;"
ob z.B. +unendlich  > MAXINT ist.
Falls das der Fall ist, so würde help_b auf MAXINT gesetzt.


Habe ich es richtig verstanden?

Gruss,
Alex

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Du hast 32 Bit integers.

da habe ich was durcheinander gebracht. Softwaremässig stehen mir 32bit
zur Verfügung, Hardwaremässig aber nur 16Bit.
Das hat aber keine Auswirkungen auf das Programm.

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ja, habe mich mißverständlich und ungenau geäußert. Der Wertebereich
der Integer ist irgendwas mit 2^32 ~ 10^9. Der Wertebereich der doubles
ist irgendwas mit +/- 10^308 bei 8 Byte, also wesentlich größer. Darauf
bezog sich meine Aussage, daß bei 'doublerechnung nix überläuft'.
Wenn bei nem double-gerechneten IIR Filter die doubles überlaufen, ist
das Filter instabil, das schwingt auf. Poste mal die Koeffizienten
o_dig_filt->A_K, daran ist das zu berechnen. Mein Post vom 24.1. bezog
sich auf die Dynamik der doubles, mein C-Programm konnte ne Eins von
einer 10^15 unterscheiden, das ist aber was anderes als der
Wertebereich.

Zerknirscht
Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Detlef,

das hier sind meine Koeffizienten:

double B_Koef[3] = {0.0011858, 0.0023716, 0.0011858};

double A_Koef[3] = {1, -1.9001381, 0.9048735};

Gruss,
Alex

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, das Filter ist stabil, z^2-1.9001381z+0.9048735 hat Nullstellen mit
Beträgen <1, das ist die Impulsantwort:
1.185800e-003
4.624784e-003
8.900529e-003
1.272739e-002
1.612995e-002
1.913245e-002
2.175873e-002
2.403215e-002
2.597550e-002
2.761099e-002

Dein C-Code ist ok. Haste z1,z2 initialisiert? Bei dem Ding läuft nix
über!

Cheers
Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Detlef,

ja, z1 und z2 sind initialisiert.

das Problem ist aber, dass mein C-Code flexibel bleiben soll, d.h. es
soll auch dann funkltionieren, falls die Koeffiozienten gändert
werden.

Gruss,
Alex

Autor: Detlef A (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ja, kein Problem, das wird schon auch mit anderen Koeffizienten
arbeiten. Draufkucken muß Du aber: Dieser Tiefpaß macht für
Gleichspannung Verstärkung 1, also z.B. steckst Du immer '100' rein
und nach dem Einschwingen kommen '100' raus. Wenn Du Deine
b-Koeffizienten allerdings z.B. Faktor 1000 hochdrehst, macht das
Filter eine Verstärkung von 1000, dann kommen für die reingesteckten
100 die Werte 100*1000 raus, da muß Du kucken, ob das in Dein Integer
paßt, in die doubles paßt das allemal. Vorsicht auch bei schmalen
Bandpässen, da kommt es dann an der Frequenz, die der Bandpaß gut
verstärkt, eventuell zum Überlauf.

Cheers
Detlef

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Hilfe!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.