Hallo! Ich hab schon mehrere Einträge im Forum zu FFT gefunden, jedoch nicht wirklich das passende. Ich will ganz simpel die Amplitude von drei Frequenzbereiche aus einem Audiosignal erhalten. Zum Beispiel der Mittelwert zwischen 80 und 250 Hz, dann der Wert von 350 bis 550 Hz und als letzten Wert zwischen 750 Hz und 1500 Hz. Die Abtastrate wäre mit 100ms voll ausreichend. Leider peil ich des mit FFT überhaupt nicht. Vielleicht ist dieses Verfahren für meine Bedürfnisse auch schwachsinnig. Deshalb brauch ich eure Hilfe. Die Schaltung soll auch so einfach wie möglich sein. Am besten ohne großen Aufwand direkt in einen ATMEGA8. Meint ihr das es funktionieren könnte? Hatte auch immer an den BA3830, ein Bandpassfilter, gedacht, aber den gibts nirgens mehr zu kaufen. Ich wäre dann über mehrere AD-Eingänge in den µC gegangen, um die Werte dann im eigentlichen Programm zu nützen. Danke im Vorraus. Simon
:
Verschoben durch Admin
Ich wurde es so machen : 1. Bestimmung von "sampling-frequency" : F (mindestens 2*1500) 2. Bestimmung von wie viele Werte pro band brauche ich. W 3. Quellcode von FFT bekommen/schreiben 100 ms maximal pro rechnung ?... toll Für 1500 Hz Maximal: 10 kHz sampling freq. 512 "samples" (512*10 us = 5,12 ms) pro rechnung gibt ein Wert je ~20 Hz, nicht schlecht. und vielleicht machbar.
4 Anregungen: BP Filter ggf. bei MAXIM scheuen andere habe aber auch welche, MAXIM schickt muster zu uni-s Analog geht das auch: da gibt-s software, die die schaltung auslegt im Internet. digi. Filter können auch mit dem uC gemacht werden und dann einfach eine max. wert-bestimmung = Ampli. des Freq. bereichs. FFT braucht ggf. nicht vollständig berechnt zu werden, wenn: - freq bekannt - abtast bekannt, etc.
Also Sampling-Frequenz wäre 3kHz, weil ja 1500 Hz maximale Frequenz. Ein Wert je 20Hz wäre sogar zu übertrieben. Denke mit 256 Samples würde es auch reichen. dann hät ich 40 Hz. Also zwischen 80 und 250Hz (sind 170) würde es mir ca. 4 Werte liefern, daraus dann den Mittelwert und ich hät was ich brauch. Mit 100ms mein ich nur, wie oft ein neuer Mittelwert akualisiert werden soll. Also wie oft die ganze Rechenoperation ausgeführt wird. Punkt 3 ist ein kniffliger Punkt. Ich kenn mich überhaupt nicht mit FFT aus. Bin deshalb nicht in der Lage, mir mal schnell einen Code zu schreiben. Des weiteren sind meine Kentnisse in Assembler <= 1%. Es wäre daher echt nett, wenn jemand einen Assambler-Code so anpassen könnte, dass dieser einfach nach dem Aufrufen drei Variablem mit dem entsprechenden Wert füllt. mfg Simon
Was Christian hat gesagt ist Korrekt, ein Digitales Filter kann im prinzip alles eingfacher machen. Gibt schon hier ein Thread. Hast du es gelesen ? (ich hab nicht) FFT Quellcode, gibt jede menge. für AVR... ich glaube gibt (ich habe für HC11 und sicher es ist nutzlich). hier: avr FFT auf asm : Beitrag "Wer kennt diesen Hersteller ?" Viel Spaß Ale
@ Axel Rühl: Was bedeutet "panzerfahr'n? ;-))" für die Leute wie ich, daß es niht können ? (kein Spaß!) Ale
Hallo Ale, klick mal selbst Deinen Link (Beitrag "Wer kennt diesen Hersteller ?") an ;-)) Da Deutsch scheinbar nicht Deine Muttersprache ist: "panzerfahr'n" ist eine (in diesem Fall humorvoll gemeinte) Verkürzung von "Panzer fahren" und bedeutet in etwa "Es würde sehr viel Spaß machen mit einem Panzer durch die Gegend zu fahren und ohne Rücksicht auf Verluste einfach alles platt zu walzen". CU
Das war blöd ! Sche***e Copy und Paste !!!!! Die richtige link war : Beitrag "FFT auf dem AVR" Ich wollte kein Spaß mit seine Frage machen !, Entschuldigung :-)
Es tut auch mir leid. Ich wollte mich nicht lustig machen. Ich wollte (auch) darauf hinweisen, dass der Link nicht richitg ist. Wenn man auf der Seite ganz nach unten scrollt, kommt ein Bild von einem Panzer. daher mein amüsante Frage. Weil es doch so garnichts mit FFT zu tun hatte. Was auf keinen Fall böse gemeint! Beitrag "Re: Wer kennt diesen Hersteller ?" sorry for that AxelR.
Na super, 9 Antworten, aber irgendwie doch nicht wirklich hilfreich. Hät mir mehr erwartet. Was mir noch bei den ganzen Schaltungen aufgefallen ist, ist der MAX293. Kann man das nicht auch ohne den machen. Grad für meine Aufgabe müsste es doch anders gehen. mfg Simon
So, ich bins nochmal. Hab grad rausgefunden, dass für meine Anwendung der Goertzel-Algorithmus voll ausreichend ist. Hab auch einen Code gefunden, der einen DTMF-Decoder entspricht.
1 | Sample code for a DTMF detector |
2 | |
3 | #define SAMPLING_RATE 8000 |
4 | #define MAX_BINS 8 |
5 | #define GOERTZEL_N 92 |
6 | |
7 | int sample_count; |
8 | double q1[ MAX_BINS ]; |
9 | double q2[ MAX_BINS ]; |
10 | double r[ MAX_BINS ]; |
11 | |
12 | /* |
13 | * coef = 2.0 * cos( (2.0 * PI * k) / (float)GOERTZEL_N)) ; |
14 | * Where k = (int) (0.5 + ((float)GOERTZEL_N * target_freq) / SAMPLING_RATE)); |
15 | * |
16 | * More simply: coef = 2.0 * cos( (2.0 * PI * target_freq) / SAMPLING_RATE ); |
17 | */ |
18 | double freqs[ MAX_BINS] = |
19 | { |
20 | 697, |
21 | 770, |
22 | 852, |
23 | 941, |
24 | 1209, |
25 | 1336, |
26 | 1477, |
27 | 1633 |
28 | }; |
29 | |
30 | double coefs[ MAX_BINS ] ; |
31 | |
32 | |
33 | /*---------------------------------------------------------------------------- |
34 | * calc_coeffs |
35 | *---------------------------------------------------------------------------- |
36 | * This is where we calculate the correct co-efficients. |
37 | */ |
38 | void calc_coeffs() |
39 | { |
40 | int n; |
41 | |
42 | for(n = 0; n < MAX_BINS; n++) |
43 | { |
44 | coefs[n] = 2.0 * cos(2.0 * 3.141592654 * freqs[n] / SAMPLING_RATE); |
45 | } |
46 | } |
47 | |
48 | |
49 | /*---------------------------------------------------------------------------- |
50 | * post_testing |
51 | *---------------------------------------------------------------------------- |
52 | * This is where we look at the bins and decide if we have a valid signal. |
53 | */ |
54 | void post_testing() |
55 | { |
56 | int row, col, see_digit; |
57 | int peak_count, max_index; |
58 | double maxval, t; |
59 | int i; |
60 | char * row_col_ascii_codes[4][4] = { |
61 | {"1", "2", "3", "A"}, |
62 | {"4", "5", "6", "B"}, |
63 | {"7", "8", "9", "C"}, |
64 | {"*", "0", "#", "D"}}; |
65 | |
66 | |
67 | /* Find the largest in the row group. */ |
68 | row = 0; |
69 | maxval = 0.0; |
70 | for ( i=0; i<4; i++ ) |
71 | { |
72 | if ( r[i] > maxval ) |
73 | { |
74 | maxval = r[i]; |
75 | row = i; |
76 | } |
77 | } |
78 | |
79 | /* Find the largest in the column group. */ |
80 | col = 4; |
81 | maxval = 0.0; |
82 | for ( i=4; i<8; i++ ) |
83 | { |
84 | if ( r[i] > maxval ) |
85 | { |
86 | maxval = r[i]; |
87 | col = i; |
88 | } |
89 | } |
90 | |
91 | |
92 | /* Check for minimum energy */ |
93 | |
94 | if ( r[row] < 4.0e5 ) /* 2.0e5 ... 1.0e8 no change */ |
95 | { |
96 | /* energy not high enough */ |
97 | } |
98 | else if ( r[col] < 4.0e5 ) |
99 | { |
100 | /* energy not high enough */ |
101 | } |
102 | else |
103 | { |
104 | see_digit = TRUE; |
105 | |
106 | /* Twist check |
107 | * CEPT => twist < 6dB |
108 | * AT&T => forward twist < 4dB and reverse twist < 8dB |
109 | * -ndB < 10 log10( v1 / v2 ), where v1 < v2 |
110 | * -4dB < 10 log10( v1 / v2 ) |
111 | * -0.4 < log10( v1 / v2 ) |
112 | * 0.398 < v1 / v2 |
113 | * 0.398 * v2 < v1 |
114 | */ |
115 | if ( r[col] > r[row] ) |
116 | { |
117 | /* Normal twist */ |
118 | max_index = col; |
119 | if ( r[row] < (r[col] * 0.398) ) /* twist > 4dB, error */ |
120 | see_digit = FALSE; |
121 | } |
122 | else /* if ( r[row] > r[col] ) */ |
123 | { |
124 | /* Reverse twist */ |
125 | max_index = row; |
126 | if ( r[col] < (r[row] * 0.158) ) /* twist > 8db, error */ |
127 | see_digit = FALSE; |
128 | } |
129 | |
130 | /* Signal to noise test |
131 | * AT&T states that the noise must be 16dB down from the signal. |
132 | * Here we count the number of signals above the threshold and |
133 | * there ought to be only two. |
134 | */ |
135 | if ( r[max_index] > 1.0e9 ) |
136 | t = r[max_index] * 0.158; |
137 | else |
138 | t = r[max_index] * 0.010; |
139 | |
140 | peak_count = 0; |
141 | for ( i=0; i<8; i++ ) |
142 | { |
143 | if ( r[i] > t ) |
144 | peak_count++; |
145 | } |
146 | if ( peak_count > 2 ) |
147 | see_digit = FALSE; |
148 | |
149 | if ( see_digit ) |
150 | { |
151 | printf( "%s", row_col_ascii_codes[row][col-4] ); |
152 | fflush(stdout); |
153 | } |
154 | } |
155 | } |
156 | |
157 | |
158 | /*---------------------------------------------------------------------------- |
159 | * goertzel |
160 | *---------------------------------------------------------------------------- |
161 | */ |
162 | void goertzel( int sample ) |
163 | { |
164 | double q0; |
165 | ui32 i; |
166 | |
167 | sample_count++; |
168 | for ( i=0; i<MAX_BINS; i++ ) |
169 | { |
170 | q0 = coefs[i] * q1[i] - q2[i] + sample; |
171 | q2[i] = q1[i]; |
172 | q1[i] = q0; |
173 | } |
174 | |
175 | if (sample_count == GOERTZEL_N) |
176 | { |
177 | for ( i=0; i<MAX_BINS; i++ ) |
178 | { |
179 | r[i] = (q1[i] * q1[i]) + (q2[i] * q2[i]) - (coefs[i] * q1[i] * q2[i]); |
180 | q1[i] = 0.0; |
181 | q2[i] = 0.0; |
182 | } |
183 | post_testing(); |
184 | sample_count = 0; |
185 | } |
186 | } |
Theoretisch müsste man den Code doch einfach nur auf meine Frequenzen umschreiben. Wollte den Code eigentlich auch in Basic haben. Außerdem weiß ich nicht, was im Code der ADC-Wert ist, und in welcher Variable die Level stehen. mfg Simon
Input -> int Sample Output Görtzel -> r[] Output Tastencode in PostProcessing() -> row_col_ascii_codes[row, col] Gruß Hagen
@Simon: "Na super, 9 Antworten, aber irgendwie doch nicht wirklich hilfreich. Hät mir mehr erwartet." Ich habe auch viele mahle etwas gefragt und nicht immer die Antwort ich habe gedach gut war und ich erwartete bekommen. Es ist so, vielleicht du hast Glück und jemand kennt ganz genau was du brauchst, manchmal nicht. Aber über was du zuerst gefragt hast glaube ich, es wäre 2 gute möglichkeiten. (Ich wurde nicht viel von Float rechnen mit ein AVR, am besten mit int, wir viel schneller sein).
Ich dachte, es ginge um die Messung von drei Amplituden, dafür ist Goertzel nicht so geeignet, eher für Schwellenüberschreitung, also "Tondecoder". Soweit ich das verstanden habe, ist ein "Goertzel" ein einfacher IIR-Bandpass, der periodisch wieder genullt wird, da er sonst überläuft. Es wird nur in diesem Zeitraum entschieden, ob die Amplitude innerhalb der Bandbreite des Filters einen Schwellenwert überschritten hat. Für einen echte Messung müßte ja ein Messwert herauskommen.
Für ein Discolauflicht reicht das allemale. Man gibt einen Wert vor und zieht dann den Takt aus der Musik. Ich denke, so etwas in der Art soll es wohl werden.
Axel Rühl wrote: > Für ein Discolauflicht reicht das allemale. > Man gibt einen Wert vor und zieht dann den Takt aus der Musik. Ich > denke, so etwas in der Art soll es wohl werden. Wenn das stimmt, dann frag ich mich doch glatt obs nicht ein paar simple RC-Filter auch tun: http://www.b-kainka.de/bastel85.htm
Es gibt da auch eine recht nette FFT Bibliothek für den AVR. (müsste nur noch den Link wiederfinden). Die Verwende ich selbst, um in einem etwas umfangreicheren DMX-Controller eine Lichtorgel und einige andere Späße zu implementieren. Da ist aber auch noch ein guter Happen Analoggekrümele auf Lochraster vornedran (das meiste davon aber nur Beaterkennung). FFT braucht aber auf jeden Fall richtig böse viel Speicher. Für die 256 Samples wären das 512 Bytes für int16_t samples. 1kB für den komplexenwertigen Eingangspuffer und ggf. nochmal 1kB für den Ausgangspuffer. Da ist dann recht schnell der RAM voll bis unter'n Rand.
Danke für die tollen Antworten! Hät schon gedacht, ich werd meine Idee bald verwerfen können. @ Hagen Re: Kann ich dann einfach die Goertzel-Funktion immer wieder (timer-gesteuert) aufrufen und der Variable Sample den Analogwert von meinem ADC-Eingang geben, also die Variable durch den Eingang ersetzten? In r[] stehen dann so wie von mir gewünscht die Amplituden? @ Axel Rühl: Genau das hab ich vor. Ein Discolauflicht. Werde dann einfach die drei Pegel von meinen Frequenzen vergleichen und dann einfach gesagt jeweils ein Licht switchen. @ Karl Heinz: Genau die Schaltung hab ich schon testweise aufgebaut. Bin aber vom Ergebnis stark enttäuscht. Entspricht leider nicht meinem Vorhaben, denn die Verbindung zum µC und die Frequenzen machen Probleme. @ Andreas Lang: Super, dass trifft auch auf mich zu. Es handelt sich später auch um einen DMX-Controller, der die Sound-to-Light-Steuerung in ordentlicher Qualität bereitstellt. Meinst du wirklich, dass der Goertzel-Algorithmus nicht ausreicht? Wer nett, wenn du doch noch die Bibliothek finden könntest. Wär cool, wenn Ihr mir auf diese Fragen auch noch Antwort geben könnt. ciao Simon
@Simon: Ich poste einfach mal hier den Code, die FFT-Bibliothek ist von Chan (elm-chan.org). Das Ganze befindet sich sehr stark im Fluss, die Hardware ist ein mittelschweres IC-Massengrab und auch sonst ist das drumherum nicht so dolle derzeit. Was man aber (denke ich mal) verwerten kann, ist der DMX-Transmitter (separater Mega8, wegen Speicherplatz) und mal die Grundsätzlichen Funktionen (Master Fader, All On, Crossfade, Interruptbsierter ADC und FFT2Light). @All: Der Code im angehängten File ist noch sehr pre-alpha. Irgendwie funktioniert er, ist aber weder gut kommentiert noch irgendwas. Ich werde aber wohl die relevanten Stellen noch hier im Forum hervorheben. Brauchbar ist schonmal der Kram im Ordner dmxtx. Das ist ein kompletter DMX-Transmitter mit SPI-Interface. Das DMX-Timing wird komplett im mit Interrupts erledigt. Der Code ist eher mäßig gut kommentiert, aber insgesamt doch recht verständlich, da nicht zu komplex. Der Kram in chaser.c ist allerdings recht wüster Spaghetticode.
Leider habs ich net so mit C. Was hats mit ffft.h auf sich? Wär echt no ah Sach, wenn du dein Code erklären könntest. Für die Datei dmxtx.c interessiere ich mich auch. Du hast geschrieben, dass man den Chip über SPI ansteuern und dadurch DMX-Daten versenden kann. Wie schaut da des Protokoll und natürlich der Schaltplan aus? mfg Simon
Der DMX-Transmitter-Code ist für einen Mega8@16MHz geschrieben. Man hängt ihn einfach 1:1 an den SPI der Master-CPU (trennbar, wenn man ISP macht) und sendet mit der Master-CPU Daten per SPI. Mit zwei Nullbytes hintereinander kann man eine aus dem Tritt geratene Übertragung wieder resetten, ansonsten: 1. Byte Kanalnummer, 2.Byte Kanalwert. Am TXD hat man dann DMX512 mit einem max 255 Kanäle großen Universe. Die größe des Universe ist im Quelltext des Transmitters angegeben und lässt sich dort leicht ändern. Der Transmittercode ist recht kompakt und sollte auch ohne große C-Kenntnisse verständlich sein. ffft.h ist die Include-/Headerdatei für die Assembler-FFT-Bibliothek (ffft.S). > Leider habs ich net so mit C. Mir gings mal ähnlich. Aber es wäre geschickt, wenn Du dich für etwas wie einen DMX-Controller in C einarbeitest, da das ganze mit BASIC, insbesondere mit BASCOM echt Tierquälerei ist. Was du aber auf jeden Fall brauchst ist viel RAM. Schon ein ganz normaler Chaser (Lauflicht) braucht bei z.B. 12 unterstützten Kanälen 12 Bytes pro Schritt. Will man das dann noch etwas intelligenter oder mit variabler Fade-Time, dann wirds noch mehr. Ein Speicherplatz für einen Chaser sollte mindestens 32 Schritte fassen können. Die sind z.B. ganz schnell weg, wenn man evtl. irgendwelche Scanner oder was auch immer damit steuern will. Für Anwendungen, bei denen nur Lampen dran sind, würden wohl auch noch 16 Positionen reichen. Ganz nett sind sogenannte Memorys und Memory Chaser. Man erstellt dann erst einzelne Lichtstimmungen (Szenen) und speichert diese. Dann packt man diese in einen Chaser. Damit kann man sehr schnell sehr hübsches Licht machen. Wenn man dann noch mehrere Chaser parallel zusammenmixen kann, ist das dann ganz edel, braucht man für die Kellerdisco aber eher nicht. Hier tun es schon 3-12 (je nachdem, wieviele Lampen man hat) unterschiedliche Chaser, die hin- und wieder mal gewechselt werden. Wenn man LED-Pars als Lampen verwenden möchte (gerade im Partykeller recht nützlich, weil die nich eben mal 0,3-1kW pro Lampe ziehen) braucht man pro Lampe schon mal 5-6 Kanäle, ein einfacher Scanner ist mit 7-11 Kanälen dabei. Deswegen gleich einen AVR mit iel RAM nehmen und Sachen, wie Bedienelemente (LC-Display, Menüführung oder was auch immer) gleich in einen extra µC packen und den per UART anbinden. Der Schaltplan ist so 'ne Sache. Das Gerät dazu ist stückchenweise um einen Mega16 herum gewachsen und verteilt sich auf 2 Ebenen von Lochrasterkarten. Das meiste darauf ist eine eher mittelmäßige Beaterkennung (Schaltplan siehe ELV-Beatcounterbausatz, auf deren Website) und was man halt sonst noch so braucht (Erzeugung der negativen Betriebsspannung für die OPs, Gleichrichter und RC-Glied fürs VU-Meter (und eine eventuelle AGC, damit man nicht immer von hand auf 0db einstellen muss; das passt dann aber in das jetzige Gehäuse nicht rein). Da das ganze in erster Linie frei Schnauze bzw "Stift und Schmierzettel" entstanden ist, werde ich wohl zunächst einmal anhand des Quelltextes die direkte Peripherie/Anschlussbelegung des Mega16 reverse-engineeren. Den Analogkram bekomm ich auch so wieder zusammen, wenn ich das Teil wieder vor mir liegen hab (liegt aber halt grad 200km entfernt). Hier mal eine grobe Übersicht, was da so drin ist: - Mega16 - Mega8 - Quarzoszi 16MHz - 3 LM324 - MAX232 - irgendwas, das zum OPA2134 pinkompatibel ist (der ist viel zu hochwertig dafür, steckt aber trotzdem im Sockel, weil ich grad nix anderes dahatte) - MC34063 oder wie er heißt - Jede menge Hühnerfutter (2 SMCC-Spulen, diverse Kondensatoren und Widerstände) - 2 Potis - 2 Kippschalter, davon einer mit Mittelstellung - 1 Duoled - 13 normale LEDs Bilder hab ich leider grad keine von. Werde aber mal welche machen, wenn ich das Gerät wieder zu fassen kriege.
Danke für die Beschreibung zum DMX-Transmitter. Ach ja, könnte mir jemand ne hex-File vom Code schicken. Hab grad kein C-Compiler parat. Und noch ne Frage: Müss ich stetig über SPI die Bytes für jeden Kanal schicken oder merkt sich der µC die letzten Settings? Output wird wohl TxD sein? Bin schon gespannt auf die Bilder und vielleicht einen groben Schaltplan. Unterstützt der ATMEGA8 eigentlich Hardware-Multiplikation und Addition? mfg Simon
Der µC merkt sich die Kanalwerte. Mit dem Code, wie er jetzt ist, hat man 128 Kanäle. Der Mega8 hat einen Hardwaremultiplizierer. Addieren kann er auch. Wenn du einen MAC (Multiply an Accumulate) meinst, den hat er nicht. Da der ATMega aber sehr schnell multiplizieren und addieren kann, kann man das in Software nachbilden. z.B. in C:
1 | uint16_t summe; |
2 | uint8_t wert[8], faktor[8]; |
3 | //Werte auffüllen
|
4 | summe=0; |
5 | for (uint8_t i=0;i<8;i++) { |
6 | summe+=((wert[i]*faktor[i])>>8); |
7 | }
|
Wobei man bei einem Lichtpult wohl eher Kanalwerte gewichtet addieren und dann den Kanalwert auf 255 begrenzen möchte:
1 | if (summe>0xff) |
2 | sume=0xff; |
Das .hex-File hänge ich mal an. Lade dir aber mal am besten WinAVR runter. Dann musst du nur noch in das Verzeichnis zu wechseln und "make" eingeben.
Ein Crossfade wäre dann so zu bauen:
1 | kanal=(((255-fade)*cch[i])>>7)+((fade*nch[i])/0xff); |
ähm, so ist es richtig:
1 | ch[i]=(((255-fade)*cch[i])/0xff)+((fade*nch[i])/0xff); |
Wobei man das ggf. mit Bitschieben schneller machen kann. Allerdings ist 255*255 um 8 Bits nach rechts geschoben nur 254. Aber das kann man wohl trotzdem als 255 gelten lassen. Dann sieht das so aus:
1 | ch[i]=(((255-fade)*cch[i])>>8)+((fade*nch[i])>>8); |
Geht jedenfalls schneller al erst mal eine richtige Division auszuführen. Man könnte auch erstmal die beiden Werte in einen Vorzeichenfreien 16-Bit Integer addieren und den dann 8 Bits nach rechts schieben. Das ist dann noch etwas schneller, da nur einmal um 8 Bits schieben nötig ist, bzw. man einfach das high-byte nimmt und gut ist. Spart wohl ein paar Takte.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.