Suche
Close this search box.

Liniengrafiken und Fourier

Die­ser klei­ne Edi­tor dient dazu, mit­tels Fourier-Transformation die Para­me­ter für Dreh­fre­quen­zen zu errech­nen, wel­che die Figur annä­hern. Er basiert auf selbst ent­wi­ckel­ten js-Klassen.

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 Koor­di­na­ten­ur­sprung, ermit­telt dann deren geo­me­tri­schen Schwer­punkt S (Durch­schnitt aller x- und aller y‑Koordinaten), und ermit­telt dar­aus dann mit atan2() den Start­win­kel pha­se und Phy­ta­goras die Län­ge amp für die­se Frequenz.

Mei­ne“ Youtube-Lehrer haben das schön gra­fisch dar­ge­stellt, bes­ser könn­te ich es nicht darstellen.

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/MaxAbstand).

Das Coding Train Video (2.) zeigt auf unter­halt­sa­me Wei­se, wie man anfängt und mit sich dre­hen­den Krei­sen eine Eisen­bahn 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 mini­mie­ren würde. 

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 einlädt. 

Ergeb­nis mei­ner Bemü­hun­gen 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).

Wie sieht die Formel aus?

Wie am Anfang erwähnt, ging es mir zunächst dar­um, die mathe­ma­ti­sche For­mel einer durch sum­mier­te Dreh­be­we­gun­gen aus­ge­drück­ten Zeich­nung mit dar­zu­stel­len. Aller­dings ist die noch sehr lang und unüber­sicht­lich. Für die Hand 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 kann Rechts-Links-Komponenten und Oben-Unten-Komponenten ent­hal­ten. 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ürde:

Chall­enge für die Beispiel-Hand eigent­lich 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?