Suche
Close this search box.

Fourier-Transformation

In den letz­ten Tagen hat­te ich an mei­nem Hobby-Javascript-Funktionsplotter „Zyklo­idulus“ wei­ter pro­gram­miert, um Lini­en­gra­fi­ken mit­tels Fourier-Transformation in ein­zel­ne Dreh­be­we­gun­gen zu zer­le­gen. Die­se Dreh­be­we­gun­gen soll­ten dann auf­ein­an­der auf­ad­diert wie­der die ursprüng­li­che Lini­en­gra­fik nach­zeich­nen. Seit heu­te klappt das:

Ich star­te­te mit einer neu­en HTML-Seite, bestehend aus dem Can­vas als Zei­chen­flä­che und ein paar But­tons, wel­che Funk­tio­nen auf­ru­fen. Im fol­gen­den Video zeich­ne ich eine Hand mit der Maus und drü­cke den fou­rier-Knopf. Das Ergeb­nis wird als Epi­zy­klo­ide (schwarz) und sich dre­hen­de, anein­an­der­ge­reih­te Vek­tor­li­ni­en (rot) dar­ge­stellt. Mit dem sub­div-Knopf füge ich zusätz­li­che Punk­te in der Lini­en­gra­fik ein. Dar­auf­hin nähert sich die dar­aus gene­rier­te Epi­zyk­le bes­ser an. Das sind die Daten, wel­che mit­tels Fourier-Transformation ermit­telt wurden:

Spektrum_Hand = [
{ freq: -35, amp: 0.0003686, phase: -1.5236009 },
{ freq: -34, amp: 0.0012953, phase: -1.1669831 },
{ freq: -33, amp: 0.0023193, phase: -1.3474052 },
{ freq: -32, amp: 0.0082433, phase: -1.4717065 },
{ freq: -31, amp: 0.0295951, phase:  0.9387542 },
{ freq: -30, amp: 0.0118085, phase: -0.1819100 },
{ freq: -29, amp: 0.0084302, phase: -0.9733754 },
{ freq: -28, amp: 0.0107256, phase: -1.6145778 },
{ freq: -27, amp: 0.0055092, phase:  0.5508575 },
{ freq: -26, amp: 0.0282102, phase: -2.0025910 },
{ freq: -25, amp: 0.0140332, phase: -2.1524231 },
{ freq: -24, amp: 0.0096098, phase: -1.3614207 },
{ freq: -23, amp: 0.0052851, phase: -0.0287800 },
{ freq: -22, amp: 0.0102071, phase: -0.8459905 },
{ freq: -21, amp: 0.0229444, phase: -0.0303723 },
{ freq: -20, amp: 0.0336553, phase: -0.6223523 },
{ freq: -19, amp: 0.0193167, phase:  0.1048640 },
{ freq: -18, amp: 0.0222801, phase: -1.1807482 },
{ freq: -17, amp: 0.0201180, phase: -0.4136481 },
{ freq: -16, amp: 0.0151423, phase: -2.1911763 },
{ freq: -15, amp: 0.0389249, phase:  1.4865131 },
{ freq: -14, amp: 0.0276480, phase:  1.0410499 },
{ freq: -13, amp: 0.0580259, phase:  1.2996377 },
{ freq: -12, amp: 0.0588539, phase:  1.2406528 },
{ freq: -11, amp: 0.0620339, phase:  0.8851068 },
{ freq: -10, amp: 0.0204838, phase: -0.1967784 },
{ freq:  -9, amp: 0.0461501, phase:  1.0957855 },
{ freq:  -8, amp: 0.0940981, phase: -2.2153818 },
{ freq:  -7, amp: 0.2368071, phase: -2.2072689 },
{ freq:  -6, amp: 0.2375195, phase: -2.4316229 },
{ freq:  -5, amp: 0.5711301, phase:  2.0492726 },
{ freq:  -4, amp: 0.4427810, phase: -2.1482357 },
{ freq:  -3, amp: 0.5485490, phase: -2.3804487 },
{ freq:  -2, amp: 0.8690513, phase: -2.2145402 },
{ freq:  -1, amp: 1.6186980, phase: -1.9505409 },
{ freq:   0, amp: 2.1221538, phase:  1.6137504 },
{ freq:   1, amp: 0.1933930, phase: -1.5236009 },
{ freq:   2, amp: 0.1692300, phase: -1.1669831 },
{ freq:   3, amp: 0.1338133, phase: -1.3474052 },
{ freq:   4, amp: 0.2651344, phase: -1.4717065 },
{ freq:   5, amp: 0.6021577, phase:  0.9387542 },
{ freq:   6, amp: 0.1644724, phase: -0.1819100 },
{ freq:   7, amp: 0.0848002, phase: -0.9733754 },
{ freq:   8, amp: 0.0809638, phase: -1.6145778 },
{ freq:   9, amp: 0.0321103, phase:  0.5508575 },
{ freq:  10, amp: 0.1297364, phase: -2.0025910 },
{ freq:  11, amp: 0.0517852, phase: -2.1524231 },
{ freq:  12, amp: 0.0288296, phase: -1.3614207 },
{ freq:  13, amp: 0.0130220, phase: -0.0287800 },
{ freq:  14, amp: 0.0208185, phase: -0.8459905 },
{ freq:  15, amp: 0.0389687, phase: -0.0303723 },
{ freq:  16, amp: 0.0477998, phase: -0.6223523 },
{ freq:  17, amp: 0.0230053, phase:  0.1048640 },
{ freq:  18, amp: 0.0222801, phase: -1.1807482 },
{ freq:  19, amp: 0.0168923, phase: -0.4136481 },
{ freq:  20, amp: 0.0106615, phase: -2.1911763 },
{ freq:  21, amp: 0.0229186, phase:  1.4865131 },
{ freq:  22, amp: 0.0135555, phase:  1.0410499 },
{ freq:  23, amp: 0.0235503, phase:  1.2996377 },
{ freq:  24, amp: 0.0196179, phase:  1.2406528 },
{ freq:  25, amp: 0.0168105, phase:  0.8851068 },
{ freq:  26, amp: 0.0044540, phase: -0.1967784 },
{ freq:  27, amp: 0.0079181, phase:  1.0957855 },
{ freq:  28, amp: 0.0124655, phase: -2.2153818 },
{ freq:  29, amp: 0.0235417, phase: -2.2072689 },
{ freq:  30, amp: 0.0170531, phase: -2.4316229 },
{ freq:  31, amp: 0.0280702, phase:  2.0492726 },
{ freq:  32, amp: 0.0137665, phase: -2.1482357 },
{ freq:  33, amp: 0.0095076, phase: -2.3804487 },
{ freq:  34, amp: 0.0066519, phase: -2.2145402 },
{ freq:  35, amp: 0.0030856, phase: -1.9505409 }
]  

71 Punk­te in der Lini­en­gra­fik erge­ben auch 71 Ein­trä­ge in die­sem Spek­trum. Jeder Ein­trag beschreibt einen sich dre­hen­den Vek­tor mit freq als Dreh­ge­schwin­dig­keit, amp als Län­ge und pha­se als Start­punkt auf dem gedach­ten Kreis. Die Zeit t läuft von 0 bis 2 π, also genau ein­mal ’rum. Bei­spiels­wei­se beschreibt der ers­te Ein­trag einen Vek­tor, der sich 35fach rechts­her­um dreht, 0,0003686 Ein­hei­ten lang ist, und bei ‑1.5236009 Radi­ant, also bei ‑87,3 Grad, mit der Dre­hung startet.

 

Wie funktioniert die Fourier-Transformation?

Ich ver­su­che es erst ein­mal mit eige­nen Wor­ten: Gege­ben ist eine Men­ge von anzahl Punk­ten P[n], also z. B. die Punk­te der Lini­en­gra­fik »Hand«. Wenn anzahl die Anzahl der Punk­te ist, läuft die Fre­quenz freq in einer Schlei­fe von -anzahl : 2 bis +anzahl : 2 mit Schritt­wei­te 1 durch. Und jetzt kommt die Magie: Man ver­dreht die Punk­te P[n] freq-mal um den Koordinatenursprung. 

Mei­ne“ Youtube-Lehrer haben das schön gra­fisch dar­ge­stellt, bes­ser könn­te ich es nicht dar­stel­len (sie­he unten).

Ver­dre­hen heißt, der Dreh­win­kel ist abhän­gig von der Ent­fer­nung des jewei­li­gen Punk­tes vom Koor­di­na­ten­ur­sprung. Der ent­fern­tes­te Punkt dreht sich z. B. bei freq = 1 genau eine vol­le Dre­hung wäh­rend einer der nur halb so weit vom Koor­di­na­ten­ur­sprung weg ist sich auch nur eine hal­be Dre­hung macht (ers­tes Video). Der Dreh­win­kel ist also freq * 2 * Pi * ( Abstand / Maximal-Abstand ).

Das Coding Train Video (2.) zeigt auf unter­halt­sa­me Wei­se, wie man anfängt und am Ende mit einer Men­ge sich dre­hen­den Krei­sen sei­ne Loko­mo­ti­ve zeich­net. Die letz­ten bei­den Vide­os sind mehr unter­halt­sa­mer Natur, zei­gen ganz anschau­lich, wie­so man mit die­sem mathe­ma­ti­schen Ver­fah­ren spie­len sollte.

Es ist ein ganz span­nen­des The­ma, denn Fourier-Transformation und das Arbei­ten mit Fre­quenz­spek­tren ermög­licht zum Bei­spiel auch die Aus­ga­be als Audio­da­tei bei gleich­zei­ti­gen Her­aus­fil­tern von zu hohen oder stö­ren­den Fre­quen­zen, was auf einem Oszil­lo­skop die Ecken abrun­den und damit das Knack­sen minimiert. 

Oszilloskopmusik-Pionier Jero­beam Fen­der­son hat mit sei­ner Musik Poly­go­ne, Ani­ma­tio­nen und klei­ne Fil­me auf einem Oszil­lo­skop erschei­nen las­sen, wenn es im soge­nann­ten X‑Y-Modus ein­ge­stellt ist. Dabei wobei die x‑Koordinate einer Zeich­nung als lin­ker und die y‑Koordinate als rech­ter Ste­reo­ka­nal aus­ge­ge­ben. Ein sehr inspi­rie­ren­der Ansatz, der zum expe­ri­men­tie­ren ein­lädt. Er ver­wen­de­te zwar dabei nicht die Fourier-Transformation, um die For­men zu glät­ten aber es ist ein inter­es­san­ter, syn­er­ge­ti­scher Ansatz, Musik zu kre­ieren. Jede Form hat eine eige­ne Klang­far­be, die Grö­ße ist die Laut­stär­ke und die Ton­hö­he ändert die auf dem Oszil­lo­skop gezeich­ne­te Form nicht, nur die Zeichengeschwindigkeit. 

Ich emp­fand es als Her­aus­for­de­rung, einen WAV-Export für mein Zei­chen­pro­gramm zu pro­gram­mie­ren. Ich woll­te wis­sen, wie das klingt, was ich zeich­ne. Das Ergeb­nis stun­den­lan­gen Tüf­telns war der wav-Button und die ent­spre­chend damit auf­ge­ru­fe­ne Funk­ti­on. Statt x- und y‑Koordinaten am Bild­schirm wer­den die Wer­te in Daten-Puffer für den lin­ken und rech­ten Audio­ka­nal aus­ge­ge­ben. Pro Kanal wer­den die Maximal- und Mini­mal­wer­te ermit­telt, dar­aus ein Ska­lie­rungs­fak­tor errech­net, um alle Wer­te auf ‑32767 … 32768 zu begren­zen. Dann wird die WAV-Datei dar­aus erzeugt. Hier ist die Ansicht auf einem Oszil­lo­skop (simu­liert mit Oscil­lo­scope! und mit dem Smart­phone abgefilmt).

Es ist übri­gens gar nicht so ein­fach, die Maximal- und Mini­mal­wer­te von aufeinander-addierten Sinus- und Cosi­nus­wel­len zu ermit­teln, denn das Ergeb­nis ist davon abhän­gig, wie­vie­le Punk­te man dabei auf dem Weg der Linie misst. Es kann immer ein höhe­rer Wert zwi­schen zwei »Sample-Punkten« lie­gen. Ich habe mir damit gehol­fen, dass ich den Maxi­mal­wert mit 0,9 mul­ti­pli­ziert habe, damit die für die WAV errech­ne­ten Wer­te beim ver­wen­de­ten Daten­typ »16-Bit-unsigned-integer« kei­nen Über­lauf erzeu­gen. Das ergibt häss­li­che Knack­ser / Über­steue­rung. Zu hören ist nun ein schö­nes Brummen.

Wie sieht die Formel aus?

Wie am Anfang erwähnt, ging es mir zunächst dar­um, die mathe­ma­ti­sche For­mel zusam­men mit der ent­ste­hen­den Zeich­nung künst­le­risch auif einem Bild dar­zu­stel­len. Aller­dings ist die noch sehr lang und unüber­sicht­lich. Für die Hand mit ihren 70 Punk­ten wür­de das so aussehen:

Auch wenn ich bereits die Kom­po­nen­te mit der Fre­quenz 0 weg­ge­las­sen habe — die ver­schiebt ja nur die gesam­te Zeich­nung auf der Ebe­ne umher — sind es noch zwei sehr lan­ge For­meln. Das Gan­ze braucht also noch ein wenig Kos­me­tik, damit es ver­ständ­lich wird und viel­leicht eines Tages mal auf ein T‑Shirt passt. Der ers­te Schritt ist das Rech­nen mit  kom­ple­xe Zah­len statt Vek­to­ren aus X und Y.

Die Koor­di­na­te eines Punk­tes lässt sich statt als Vek­tor aus zwei Koor­di­na­ten (lin­kes Bild) auch als kom­ple­xe Zahl beschrei­ben (rechts). Vor­teil: Die Sum­men­for­mel aus Kreis­be­we­gun­gen ent­hält bei­de Kom­po­nen­ten: Rechts-Links und Oben-Unten. Letz­te­re wer­den mit i mul­ti­pli­ziert. Mer­ke: Mul­ti­pli­zie­ren mit i dreht um 90 Grad nach links um den Koor­di­na­ten­ur­sprung. z( t ) ist die Funk­ti­on, wel­che die Hand in ein Koor­di­na­ten­sys­tem auf die kom­ple­xe Zah­len­ebe­ne zeich­nen wür­de, wenn t von 0 bis 2 * Pi läuft:

Chall­enge für die Beispiel-Hand, alles in nur eine For­mel zu schrei­ben, im Prin­zip erfüllt. Doch da geht noch was.

Der nächs­te Schritt ist das Erset­zen von a*(cos(x)+i*sin(x)) durch eine Funk­ti­on a*cis(x). So kom­men die Dopp­lun­gen raus, da man ja den Win­kel in cos() und sin() schrei­ben muss. So kom­men die Ter­me in der Klam­mer nur ein­mal vor. Das erspart etwa die Hälfte.

Das ist nun die For­mel für eine Funk­ti­on, wel­che die­se Beispiel-Hand in ein Koor­di­na­ten­sys­tem zeich­net. Sieht doch schon­mal ganz gut aus, oder?