Suche
Close this search box.

Liniengrafiken und Fourier

In den letzten Tagen hatte ich an meinem Hobby-Javascript-Funktionsplotter „Zykloidulus“ weiter programmiert, um Liniengrafiken mittels Fourier-Transformation in einzelne Drehbewegungen zu zerlegen. Diese Drehbewegungen sollten dann aufeinander aufaddiert wieder die ursprüngliche Liniengrafik nachzeichnen. Seit heute klappt das:

Ich startete mit einer neuen HTML-Seite, bestehend aus dem Canvas als Zeichenfläche und ein paar Buttons, welche Funktionen aufrufen. Im folgenden Video zeichne ich eine Hand mit der Maus und drücke den fourier-Knopf. Das Ergebnis wird als Epizykloide (schwarz) und sich drehende, aneinandergereihte Vektorlinien (rot) dargestellt. Mit dem subdiv-Knopf füge ich zusätzliche Punkte in der Liniengrafik ein. Daraufhin nähert sich die daraus generierte Epizykle besser an. Das sind die Daten, welche mittels Fourier-Transformation ermittelt 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 Punkte in der Liniengrafik ergeben auch 71 Einträge in diesem Spektrum. Jeder Eintrag beschreibt einen sich drehenden Vektor mit freq als Drehgeschwindigkeit, amp als Länge und phase als Startpunkt auf dem gedachten Kreis. Die Zeit t läuft von 0 bis 2 π, also genau einmal ‘rum. Beispielsweise beschreibt der erste Eintrag einen Vektor, der sich 35fach rechtsherum dreht, 0,0003686 Einheiten lang ist, und bei -1.5236009 Radiant, also bei -87,3 Grad, mit der Drehung startet.

 

Wie funktioniert die Fourier-Transformation?

Ich versuche es erst einmal mit eigenen Worten: Gegeben ist eine Menge von anzahl Punkten P[n], also z. B. die Punkte der Liniengrafik “Hand”. Wenn anzahl die Anzahl der Punkte ist, läuft die Frequenz freq in einer Schleife von –anzahl/2 bis +anzahl/2 mit Schrittweite 1 durch. Und jetzt kommt die Magie: Man verdreht die Punkte P[n] freq-mal um den Koordinatenursprung, ermittelt dann deren geometrischen Schwerpunkt S (Durchschnitt aller x- und aller y-Koordinaten), und ermittelt daraus dann mit atan2() den Startwinkel phase und Phytagoras die Länge amp für diese Frequenz.

„Meine“ Youtube-Lehrer haben das schön grafisch dargestellt, besser könnte ich es nicht darstellen.

Verdrehen heißt, der Drehwinkel ist abhängig von der Entfernung des jeweiligen Punktes vom Koordinatenursprung. Der entfernteste Punkt dreht sich z. B. bei freq = 1 genau eine volle Drehung während einer der nur halb so weit vom Koordinatenursprung weg ist sich auch nur eine halbe Drehung macht (erstes Video). Der Drehwinkel ist also freq*2*Pi*(Abstand/MaxAbstand).

Das Coding Train Video (2.) zeigt auf unterhaltsame Weise, wie man anfängt und mit sich drehenden Kreisen eine Eisenbahn zeichnet. Die letzten beiden Videos sind mehr unterhaltsamer Natur, zeigen ganz anschaulich, wieso man mit diesem mathematischen Verfahren spielen sollte.

Es ist ein ganz spannendes Thema, denn Fourier-Transformation und das Arbeiten mit Frequenzspektren ermöglicht zum Beispiel auch die Ausgabe als Audiodatei bei gleichzeitigen Herausfiltern von zu hohen oder störenden Frequenzen, was auf einem Oszilloskop die Ecken abrunden und damit das Knacksen minimieren würde. 

Oszilloskopmusik-Pionier Jerobeam Fenderson hat mit seiner Musik Polygone, Animationen und kleine Filme auf einem Oszilloskop erscheinen lassen, wenn es im sogenannten X-Y-Modus eingestellt ist. Dabei wobei die x-Koordinate einer Zeichnung als linker und die y-Koordinate als rechter Stereokanal ausgegeben. Ein sehr inspirierender Ansatz, der zum experimentieren einlädt. 

Ergebnis meiner Bemühungen war der wav-Button und die entsprechend damit aufgerufene Funktion. Statt x- und y-Koordinaten am Bildschirm werden die Werte in Daten-Puffer für den linken und rechten Audiokanal ausgegeben. Pro Kanal werden die Maximal- und Minimalwerte ermittelt, daraus ein Skalierungsfaktor errechnet, um alle Werte auf -32767 … 32768 zu begrenzen. Dann wird die WAV-Datei daraus erzeugt. Hier ist die Ansicht auf einem Oszilloskop (simuliert mit Oscilloscope! und mit dem Smartphone abgefilmt).

Wie sieht die Formel aus?

Wie am Anfang erwähnt, ging es mir zunächst darum, die mathematische Formel einer durch summierte Drehbewegungen ausgedrückten Zeichnung mit darzustellen. Allerdings ist die noch sehr lang und unübersichtlich. Für die Hand würde das so aussehen:

Auch wenn ich bereits die Komponente mit der Frequenz 0 weggelassen habe – die verschiebt ja nur die gesamte Zeichnung auf der Ebene umher – sind es noch zwei sehr lange Formeln. Das Ganze braucht also noch ein wenig Kosmetik, damit es verständlich wird und vielleicht eines Tages mal auf ein T-Shirt passt. Der erste Schritt ist das Rechnen mit  komplexe Zahlen statt Vektoren aus X und Y.

Die Koordinate eines Punktes lässt sich statt als Vektor aus zwei Koordinaten (linkes Bild) auch als komplexe Zahl beschreiben (rechts). Vorteil: Die Summenformel kann Rechts-Links-Komponenten und Oben-Unten-Komponenten enthalten. Letztere werden mit i multipliziert. Merke: Multiplizieren mit i dreht um 90 Grad nach links um den Koordinatenursprung. z( t ) ist die Funktion, welche die Hand in ein Koordinatensystem, auf die komplexe Zahlenebene zeichnen würde:

Challenge für die Beispiel-Hand eigentlich erfüllt. Doch da geht noch was.

Der nächste Schritt ist das Ersetzen von a*(cos(x)+i*sin(x)) durch eine Funktion a*cis(x). So kommen die Dopplungen raus, da man ja den Winkel in cos() und sin() schreiben muss. So kommen die Terme in der Klammer nur einmal vor. Das erspart etwa die Hälfte.

Das ist nun die Formel für eine Funktion, welche diese Beispiel-Hand in ein Koordinatensystem zeichnet. Sieht doch schonmal ganz gut aus, oder?