Kolophon
Ein Script zur Berechnung von Obertonspektren
Kapitelinhalt: [ Überspringen ]Das folgende Kapitel beschreibt ein Script, mit dem die Verzerrungen eines Sinussignals an einer gegebenen statischen Kennlinie untersucht werden können. Das heißt, Sinussignale mit wachsendem Pegel werden an einer gegebenen Kennlinie verzerrt und dann die Obertonverteilung im verzerrten Signal in Abhängigkeit vom Pegel errechnet und in einem Diagramm dargestellt.
Der Artikel dient dabei nicht dazu, dass Script selbst zu verteilen (im Sinne von Open Source) – dafür ist das Script viel zu sehr „Heimwerkerprogrammierung“ und auch zu konkret auf eine Anwendung beschränkt – sondern nur, die Ergebnisse dieses Scriptes für den Leser nachvollziehbar zu machen.
Mathematische Hintergründe
Kapitelinhalt: [ Überspringen ]Das Script hat die Aufgabe, eine Zahlenfolge, die ein sinusförmiges Signal einer Periodenlänge und verschiener Amplitude darstellt, an einer ebenfalls durch Wertepaare repräsentierten statischen und zeitinvarianten Kennlinie zu „verzerren„ und die das verzerrte Signal darstellende Zahlenfolge mit Hilfe einer numerisch nachgebildeten Fouriertransformation auf seinen Klirrfaktor und die Zusammensetzung der entstandenen Obertöne zu untersuchen. Dazu im Folgenden zunächst die mathematischen Hintergründe.
Die Fourier-Transformation
Begonnen wird mit der Definition eines periodischen Signals entsprechend der Fouriertransformation (siehe [ Goehler ]) – ein periodisches Signal lässt sich als Summe der Sinus- und Cosinusfunktionen seiner Grundfrequenz und der der n-fachen Frequenzen definieren:
\( \begin{eqnarray} f(x)& = & \frac{a_0}{2} + \sum_{\textrm{n=1}}^{\infty} {\big [} a_{\textrm{n}}\cos (n x)+ b_{\textrm{n}}\sin (n x) {\big ]} \textrm{d} x \tag{1}\end{eqnarray} \)
Die erstgenannte Gleichung 1 muss nun noch „auf Elektrotechnik umgebaut“ werden (wobei die Spannung U1 aus der einheitenlosen Gleichung 1 eine einheitenbehaftete Gleichung macht – etwas wie U1 = 1 V würde dem Genüge tun):
\( \begin{eqnarray} u_{\textrm{A}}(t) & = & U_{\textrm{1}}\!\cdot{}\!\! \left( \frac{a_0}{2} \!+\! \sum_{\textrm{n=1}}^{\infty} {\big [} b_{\textrm{n}}\!\cdot{}\!\!\sin (n\!\cdot{}\!ωt) + a_{\textrm{n}}\!\cdot{}\!\!\cos (n\!\cdot{}\!ωt) {\big ]} \textrm{d} x \right) \tag{2}\end{eqnarray} \)
Die Koeffizienten für Gleichanteil, den Grundton wie die einzelnen Obertöne lassen sich (im Zeitbereich) durch die Multiplikation des Signals mit dem entsprechenden Sinus- oder Cosinussignals und die anschließende Integration über eine Periodendauer ermitteln:
\( \begin{eqnarray} a_0 & = & \frac{1}{U_1} \cdot{} \frac{2}{ω} \int_{\textrm{-π}}^{π} {\big [} u_{\textrm{E}}(t) \cdot{} \cos (0\cdot{} ωt) {\big ]} \textrm{d} t \\~\\ & = & \frac{1}{U_1} \cdot{} \frac{2}{ω} \int_{\textrm{-π}}^{π} u_{\textrm{E}}(t)\textrm{d} t \\~\\ b_0 & = & \frac{1}{U_1} \cdot{} \frac{2}{ω} \int_{\textrm{-π}}^{π} {\big [} u_{\textrm{E}}(t) \cdot{} \sin (0\cdot{} ωt) {\big ]} \textrm{d} t \\~\\ & = & 0 \\~\\ a_{\textrm{n}} & = & \frac{1}{U_1} \cdot{} \frac{2}{ω} \int_{\textrm{-π}}^{π} {\big [} u_{\textrm{E}}(t) \cdot{} \cos (n\cdot{} ωt) {\big ]} \textrm{d} t \\~\\ b_{\textrm{n}} & = & \frac{1}{U_1} \cdot{} \frac{2}{ω} \int_{\textrm{-π}}^{π} {\big [} u_{\textrm{E}}(t) \cdot{} \sin (n\cdot{} ωt) {\big ]} \textrm{d} t \tag{3}\end{eqnarray} \)
Die numerische Umsetzung
Obige Gleichung 3 muss jetzt für eine numerische Rechnung umgestellt werden – aus dem Integral über eine Periodendauer des Grundtones wird jetzt wieder eine Summe für 360 Werte (statt von null bis 2π wird der Einfachheit halber von 0° bis 360° gerechnet), wobei die Werte für 0° und 360° halb gewichtet werden müssen:
\( \begin{eqnarray} a_{\textrm{n}} & = & \frac{1}{U_1} \cdot{} \frac{1}{180} \cdot{} {\Big [} \frac{1}{2}\cdot{} u_{\textrm{A}}[0] \cdot{} \cos(n\cdot{}0) + \\~\\&& \sum_{\textrm{i=1}}^{360} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{2π} {360} \cdot{}n\cdot{}i {\Big )} + \\~\\&& \frac{1}{2}\cdot{} u_{\textrm{A}}[360] \cdot{} \cos {\Big (} \frac{2π} {360} \cdot{}n\cdot{}360 {\Big )} {\Big ]} \tag{4}\end{eqnarray} \)
Da davon auszugehen ist, dass sowohl beim Eingangssignal als auch bei den Sinussignalen der Wert für 0° gleich dem für 360° ist, können die beiden halben Randwerte gleichgesetzt und deren Summe in die „große Summe“ genommen werden:
\( \begin{eqnarray} u_{\textrm{A}}[0] & = & u_{\textrm{A}}[360] \\~\\ \cos(0) & = & \cos {\Big (} \frac{2π} {360} \cdot{}n\cdot{}360 {\Big )} = \cos(n\cdot{}2π) = 1 \\~\\ a_{\textrm{n}} & = & \frac{1}{U_1} \cdot{} \frac{1}{180} \cdot{} {\Big [} \frac{1}{2}\cdot{} u_{\textrm{A}}[0] \cdot{} \cos(0) + \\~\\&& \sum_{\textrm{i=1}}^{360} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{2π} {360} \cdot{}n\cdot{}i {\Big )} + \\~\\&& \frac{1}{2}\cdot{} u_{\textrm{A}}[360] \cdot{} \cos {\Big (} \frac{2π} {360} \cdot{}n\cdot{}i {\Big )} {\Big ]} \\~\\ & = & \frac{1}{U_1} \cdot{} \frac{1}{180} \cdot{} \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{π} {180} \cdot{}n\cdot{}i {\Big )} {\Big ]} \tag{5}\end{eqnarray} \)
Das Gleiche noch einmal für die Sinus-Parameter:
\( \begin{eqnarray} u_{\textrm{A}}[0] & = & u_{\textrm{A}}[360] \\~\\ \sin(0) & = & \sin {\Big (} \frac{2π} {360} \cdot{}n\cdot{}360 {\Big )} = \sin(n\cdot{}2π) = 0 \\~\\ b_{\textrm{n}} & = & \frac{1}{U_1} \cdot{} \frac{1}{180} \cdot{} {\Big [} \frac{1}{2}\cdot{} u_{\textrm{A}}[0] \cdot{} \sin(0) + \\~\\&& \sum_{\textrm{i=1}}^{360} u_{\textrm{A}}[i]\cdot{} \sin {\Big (} \frac{2π} {360} \cdot{}n\cdot{}i {\Big )} + \\~\\&& \frac{1}{2}\cdot{} u_{\textrm{A}}[360] \cdot{} \sin {\Big (} \frac{2π} {360} \cdot{}n\cdot{}i {\Big )} {\Big ]} \\~\\ & = & \frac{1}{U_1} \cdot{} \frac{1}{180} \cdot{} \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \sin {\Big (} \frac{π} {180} \cdot{}n\cdot{}i {\Big )} {\Big ]} \tag{6}\end{eqnarray} \)
Mit diesen Gleichungen gerechnet würde man für den Grundton wie für jede Harmonische zwei Koeffizienten erhalten, wobei sich in der Verteilung auf bn und an die jeweilige Phasenlage von Grundton und Harmonischen wiederspiegelt. Für die Ermittlung des Betrages der Signalanteile muss jeweils die Wurzel der Quadratesumme beider Koeffizienten ermittelt werden.
Die (aussteuerungsunabhängigen) Koeffizienten für die einzelnen Obertöne wiederum ergeben sich aus dem Verhältnis der Leistung eines Obertones zur Leistung des Gesamtsignals; d. h. auch hier muss quadratisch addiert werden.
\( \begin{eqnarray} a_{\textrm{k,n}} & = & \frac{\sqrt{a_{\textrm{n}}^2 + b_{\textrm{n}}^2 } } {\sqrt{ a_1^2 + b_1^2 + a_2^2 + b_2^2 + a_3^2 + b_3^2 + … + a_{\textrm{m}}^2 + b_{\textrm{m}}^2 } } \\~\\ & = & \sqrt{ \frac{a_{\textrm{n}}^2 + b_{\textrm{n}}^2 } { \sum_{\textrm{l=1}}^m { \left( a_{\textrm{l}}^2 + b_{\textrm{l}}^2 \right) } } } \tag{7}\end{eqnarray} \)
Jetzt können Gleichung 5 und Gleichung 6 eingesetzt werden. Dadurch, das an und bn in Zähler und Nenner in gleicher Potenz (Wurzel aus der Quadratesumme) vorliegen, entfallen sämtliche linearen Faktoren vor den Integralen bzw. Summen – d. h. die Teiler 1 / 180 und 1 /U1 – und Gleichung 7 lässt sich zu etwas monströs erscheinendem, leicht iterierbarem zusammenfassen
\( \begin{equation} a_{\textrm{k,n}} = \sqrt{ \left( \cfrac{1}{180} \cdot{} \cfrac{1}{U_1} \right)^2 } \cdot{} \sqrt{ \left( 180 \cdot{} U_1 \right)^2 } \cdot{} \\~\\ \sqrt{ \frac{ \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{π\cdot n\cdot i} {180} {\Big )} {\Big ]} \right)^{2} + \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \sin {\Big (} \frac{π\cdot n\cdot i } {180} {\Big )} {\Big ]} \right)^{2} } { \sum_{\textrm{l=1}}^{m} {\Big [} \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{π\cdot l\cdot i } {180} {\Big )} {\Big ]} \right)^{2} + \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \sin {\Big (} \frac{π\cdot l\cdot i } {180} {\Big )} {\Big ]} \right)^{2} \,{\Big ]} } } \tag{8}\end{equation} \)
wobei m im Script über die Programmvariable
maxOvertones
auf 15 gesetzt wurde.
Vergleichbares gilt für den Klirrfaktor:
\( \begin{equation} k = \\~\\ \sqrt{ \frac{ \sum_{\textrm{l=2}}^{m} {\Big [} \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{π\cdot l\cdot i} {180} {\Big )} {\Big ]} \right)^{2} + \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \sin {\Big (} \frac{π\cdot l\cdot i } {180} {\Big )} {\Big ]} \right)^{2} \,{\Big ]} } { \sum_{\textrm{l=1}}^{m} {\Big [} \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \cos {\Big (} \frac{π\cdot l\cdot i } {180} {\Big )} {\Big ]} \right)^{2} + \left( \sum_{\textrm{i=0}}^{360} {\Big [} u_{\textrm{A}}[i]\cdot{} \sin {\Big (} \frac{π\cdot l\cdot i } {180} {\Big )} {\Big ]} \right)^{2} \,{\Big ]} } } \tag{9}\end{equation} \)
Diesen Formeln beschreiben das, was am Ende umgesetzt werden muss, im Script wird jedoch wieder zurück in Einzelschritte zerlegt. Soll heißen, bei der numerischen Umsetzung obiger Gleichungen wird natürlich rationell gearbeitet – zum einen werden alle Werte für alle Sinus- und Cosinusfolgen sin(π ⋅ n ⋅ i / 360) und cos(π ⋅ n ⋅ i / 360) genau einmal „vorgekocht“, zum anderen werden auch die Koeffizienten an und bn für jedes n einmal unskaliert berechnet und dann für die Berechnung jedes Werts von ak,n und k wiederverwendet.
Außerdem entfällt – wie auch in Gleichung 8 und Gleichung 9 bereits sichtbar – die Skalierung der Koeffizienten an und bn über die Division durch U1 wie auch durch den Wert 180, da die einzelnen Koeffizienten ohnehin durcheinander dividiert werden.
Kennlinienapproximation
Last but not least die Ermittlung der
Ausgangswertefolge der Verzerrung an der Kennlinie durch lineare
Approximation –
die Kennlinie, an der das Eingangssignal „verzerrt“
wird, muss als Array von Wertepaaren
[x[i], y[i]]
vorliegen (wobei die x-Werte das Eingangssignal
und die y-Werte für das zugehörige Ausgangssignal
repräsentieren).
Mit jedem Augenblickswert des Eingangssignals uE[i] wird nun das Kennlinienarray durchsucht, bis sich ein x[j] ≤ uE[i] und x[j+1] > uE[i] findet – damit wird der zugehörige Augenblickswert des Ausgangssignals approximiert:
\( \begin{equation} \textrm{Wenn}~~ u_{\textrm{E}} ≥ x[j] ~~\textrm{und}~~ u_{\textrm{E}}[i] ≤ x[j+1] \textrm{ :} \\~\\ u_{\textrm{A}} = y[j] + \cfrac{u_{\textrm{E}} - x[j]} {x[j+1] - x[j]} \cdot {\Large (} y[j+1] - y[j] {\Large )} \tag{10}\end{equation} \)
Obige
Gleichung 10
setzt natürlich voraus, dass der unter
x[j+1]
hinterlegte Wert
immer größer oder zumindest gleich dem unter
x[j]
hinterlegten Wert ist, das heißt, dass die Kennlinie
„von links nach rechts“ notiert bzw. sortiert ist.
Das Script kennl_k2k3.py
Kapitelinhalt: [ Überspringen ]
Nach der Darstellung der mathematischen Hintergründe ein Überblick über die Umsetzung in Form des Python-Scripts
kennl_k2k3.py
.
Signaldarstellung
Zunächst eine grundsätzliche Anmerkung zur Darstellung von Signalen – Signale mit der Dauer einer Phasenlänge des Grundtones wie beispielsweise das Eingangssignal werden im Zeitbereich numerisch in einer Werteliste von 361 Werten (null bis einschließlich 360 Grad – die in Gleichung 5 und Gleichung 6 angedeutete Vereinfachung wurde im Script nicht mehr umgesetzt) dargestellt. Im Falle des Eingangssignals handelt es sich um eine Liste der Werte einer Sinusfunktion von null bis 360 Grad.
Diese das Eingangssignal repräsentierende Liste kann jetzt
zunächst skaliert
(Parameter gain
)
und um einen absoluten Betrag verschoben (Parameter
offset
) werden, um dann als
Argument für die Verzerrung durch eine mathematische Funktion
(z. B. der Tangens Hyperbolicus für die
Eingangsstufe eines OTA) oder für die Verformung an einer
Kennlinie zu dienen.
Diese Signalliste wird für jede Einstellung des Parameters Gain (des Eingangssignalpegels) erstellt und verzerrt / verformt; Ergebnis ist ein Liste von Signallisten – die Ausgangssignale der Verzerrung / Verformung bei verschiedenen Gain-Stufen.
Um die Koeffizienten der verschiedenen Obertöne zu ermitteln, wird zunächst einmal ein Liste von jeweils zwei Signallisten bzw. einem Listenpaar erstellt – d. h. für den Grundton und jede Harmonische je eine Signalliste mit dem Sinus und mit dem Cosinus der entsprechenden Frequenz.
In der Funktion getOvertoneValue
, die eine
Fouriertransformation nachbildet, wird nun
das „verzerrte Signal“ (bzw. dessen
Signalliste) jedes Pegels elementeweise
mit den Sinus- und Cosinuslisten des Grundtons und jedes
Obertons multipliziert, die Produkte dieser elementeweisen
Multiplikation getrennt für Sinus und Cosinus aufsummiert, die
Summen (für Sinus und Cosinus für jeden Grund-
und Oberton für jedes Gain) einzeln quadriert
und anschließend der werte für Sinus und Cosinus zu einem Koeffizienten
addiert.
Der in dieser Funktion ermittelte Koeffizient für dieses Gain und diesen Oberton wird durch die Summe aller Koeffizienten für dieses Gain und für alle Obertöne einschließlich des Grundtons dividiert – die Wurzel dieses Quotienten bildet dann den Parameter ak,n – die Wurzel als dem Quotienten der Summe der Koeffizienten aller Obertöne (ohne Grundton) dividiert durch der Summe der Koeffizienten aller Obertöne (einschließlich des Grundtons) wiederum bildet den Klirrfaktor. (Siehe dazu auch obige Gleichung 8 und Gleichung 9).
Beim Summieren der Elementeprodukte wird das erste und das letzte Produkt jeweils halbiert – die in Gleichung 5 und Gleichung 6 angedeuteten Vereinfachung wurden, wie gesagt, im Script nicht mehr umgesetzt.
Voreinstellungen
minGain = -40
: Geringster Eingangssignalpegel [dBV].maxGain = 20
: Höchster Eingangssignalpegel [dBV].gainStepWdth = 1
: Abstand der Gainwerte, für die die Obertöne berechnet werden.gain = 0
: Eingangssignalpegel.nbrs = 360
: Zahl der Messwerte.offset = 0
: Gleichspannungsoffset für die Sinusfunktion.maxOvertones = 15
: Höchster zu berechneter Oberton.
Globale Variablen
sinuscosinus = []
: Zwei Signallisten (sin(x), cos(x)) des Grundtones oder einer Oberwelle.fourierMtrx = []
: Liste von Signallisten-Paare der verschiedenen Obertöne.out = []
: Signalliste der Werte eines Signals nach der Verzerrung.outs = []
: Array der Signallisten der Signale jeden Pegels nach der Verzerrung.OvertoneValues = []
: Liste der Pegel einer Oberwelle bei den verschiedenen Gainwerten.ListOfOvertones = []
: Liste der Ausgangswerte-Listen aller Obertöne.
Dateiformate
Um das Script für mehrere Kennlinien einsetzen zu können, musste zumindest für diese Kennlinien selbst eine Art Dateiformat für eine JSON-Datei festgelegt werden – die Kennliniendatei:
Kennliniendatei
Die Kennlinien werden als Sammlung von Kennlinienpunkten in eine
JSON-Datei eingetragen: Neben der eigentlichen, im Array
punkte
eingetragenen Kennlinie enthält die Datei weitere Informationen:
name
: Name der Kennlinie, z.B. für Name der Diagramm-Bilddateititle
: Diagrammtitelxtitle
undytitle
: Achsentitle für das Diagrammxmin
,ymin
,xmax
undymax
: Minimal- und Maximalwerte der KennliniexAP
undyAP
: Lage des Arbeitspunktes auf der Kennliniegain
: Skalierung für das zu verzerrende Eingangssignal, im Standardfall gleich eins (wichtig ist das Vorzeichen)punkte
: Liste von Kennlinienpunkten – d. h. Liste von Wertepaaren mit Ein- und Ausgangswert; d. h. Liste von zwei-Elemente-Listen. Eine entsprechende JSON-kompatible Folge von Wertepaaren kann in EXCEL mit der VERKETTEN-Funktion wie folgt erstellt werden:
VERKETTEN(" , [ ";
<x-Wert>" , ";
<y-Wert>;" ]")
Also z. B.:
VERKETTEN(" [ [ ";B1;" , ";C1;" ]")
VERKETTEN(" , [ ";B2;" , ";C2;" ]")
VERKETTEN(" , [ ";B3;" , ";C3;" ]")
…
(Am Ende die erste Klammer wieder schließen.)plotlist
: Liste der Graphen für die verschiedenen Harmonischen; Liste hat folgende Parameter:
harmon
: Nummer der Harmonischenannotate
: Label, das an den Graphen geschrieben werden sollcolor
: Farbe des Graphenlinestyle
: Linienart des Graphenlinewidth
: Linienbreite des Graphenxy
: Position des Labels, das an den Graphen geschrieben werden soll – muss (leider) im fertigen Diagramm händisch gesetzt werden.
Literaturhinweise
- [ Goehler ]
Goehler, Wilhelm: Formelsammlung höhere Mathematik / zsgst. von Wilhelm Goehler, Bearb. von Barbara Ralle, 14., überarb. Auflage; Verlag Harri Deutsch, Thun und Frankfurt am Main, 1999; S. 50