Mittlere Phasenverschiebung zweier Sinuswellen berechnen

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
gotridofmyphone
User
Beiträge: 33
Registriert: Mittwoch 15. März 2017, 08:54

Sonntag 30. Dezember 2018, 12:48

Hallo,

ich habe folgendes Problem, dass ich der Kürze halber in Code ausdrücke. Wo liegt mein Denkfehler?

Code: Alles auswählen

import numpy as np

Tau = 2*np.pi
iseq = np.arange(44100)
def osc(freq, phase):
    return np.sin(
        Tau * (iseq * freq / iseq.size + phase/360)
    )

signal = osc(440, 40)
sinphasor = osc(440, 0)
cosphasor = osc(440, 90)

def segments(array, resolution=100):
    samples_per_segment = int(iseq.size / resolution + 0.5)
    out = list()
    position = 0
    for i in range(resolution):
        segment_sum = sum(array[
            position:position+samples_per_segment
        ])
        out.append(segment_sum / samples_per_segment)
        position += samples_per_segment
    return np.array(out)

sinprobe = segments(signal * sinphasor)
cosprobe = segments(signal * cosphasor)

print("Mittlere Phase ist {}, aber ich hätte {} erwartet".format(np.mean(np.arctan2(sinprobe, cosprobe)) / Tau * 360, 40))
print("Ditto beim ersten Element des Eingangssignals: {} != {}".format(np.arctan2(np.sin(signal[0]), np.cos(signal[0])) / Tau * 360, 40))
print("Aber warum geht dann das? {} == {}".format(np.arctan2(np.sin(Tau * 40/360), np.cos(Tau * 40/360)) / Tau * 360, 40))

Falls Kontext interessiert:

Ich betreibe Soundanalyse, nachdem die Soundsynthese¹, bewusst weder modular noch plugin/filter-basiert (die Natur funktioniert auch nicht so), aber nicht in Echtzeit (obwohl in der Natur das in Echtzeit abläuft), schon recht gut funktioniert.
Üblicherweise verwendet man dazu Fast-Fourier-Transformation und, wenn die zeitliche Klangentwicklung wichtig ist, Wavelet-Spektrogramme. Ist mir zu ungenau, um motiviert zu sein, das nachzunutzen oder so nachzuprogrammieren, dass mir Werte ausgegeben werden statt Farbcodierungen.

Lieber will ich das ganze eh von Grund auf verstehen, indem ich das selber mache. Ich suche nach Wegen, die Soundanalyse detaillierter aufzuziehen. So stell ich mir das vor: Dem Analysetool in spe gebe ich also eine Sounddatei und eine Instrumentendefinition in Verbindung mit den Eigenschaften der zu spielenden Note. Das Programm gibt dann eine 3D-Säulen-Graphik aus, der die Unterschiede zwischen dem Klang, der gemäß der Definition und der Noteneigenschaften gerendert würde, und dem Vergleichsklang visuell interpretierbar macht und es somit ermöglicht, die Instrumentendefinition an ein gegebenes Soll anzugleichen und mich dabei nicht allein auf meine Ohren zu verlassen. Wenn ich mit der angepassten Instrumentendefinition und bestimmten Noteneigenschaften Audiodaten generiere, und diese Audiodaten wiederum als Eingangssignal und dieselbe Instrumentdefinition und die Noteneigenschaften als Parameter an das fragliche Analysetool verfüttere, muss es, wenn es richtig funktioniert, einen rechteckigen Wald gleich hoher, einfarbiger Säulen ausgeben. Eine Seite des Rechtecks ist die Frequenzdomäne, die andere die Zeitdomäne, die Höhe der Säulen ist die Amplitude, die durch die Amplitude der Sollhüllkurve des jeweiligen Teiltons geteilt wird, also ideal =1. Entspricht der Sound nicht exakt dem Sollzustand, haben die Säulen zum Beispiel einen andersfarbigen Anteil, der über die momentane Abweichung von der mittleren Phase Auskunft gibt. Da ich also die Phase verfolgen muss, komme ich mit imaginären Zahlen, die sie wegabstrahieren würden, nicht weiter.

_________
Off-Topic:
¹) Programmierer und (sehr) nerdige Musiker könnten an meinem OSS-Projekt Sompyler oder an der Mitarbeit daran interessiert sein.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Sonntag 30. Dezember 2018, 13:07

Mit Arkustangens kannst du nicht die Phasenverschiebung berechnen. Das kannst du mit

Code: Alles auswählen

print(np.rad2deg(np.arctan2(sinprobe, sinprobe)))
ausprobieren. Die Phasenverschiebung einer Kurve zu sich selbst sollte 0 sein, es kommt aber 45° raus.
a fool with a tool is still a fool, www.magben.de, YouTube
gotridofmyphone
User
Beiträge: 33
Registriert: Mittwoch 15. März 2017, 08:54

Sonntag 30. Dezember 2018, 13:52

Sieht mir nach einem Rechenfehler aus. Klar kommen falsche Ergebnisse, wenn du ihm Sinus anstelle Cosinuswerten gibst. Oder blick ich nicht, worauf du hinaus willst?
Andere Frage, die aber vielleicht dasselbe Problem aus anderer Perspektive beleuchtet: Warum bekomme ich beim dritten print() das erwartete Ergebnis, beim zweiten aber nicht?
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Sonntag 30. Dezember 2018, 14:36

OK war Denkfehler von mir.
Aber der Fehler zeigt warum bei dir immer 45° rauskommt:

Code: Alles auswählen

print( np.all(sinprobe==cosprobe)) # True

import matplotlib.pyplot as plt
plt.plot(sinphasor)
plt.plot(cosphasor)
plt.show()
Das ist so, weil immer die Phase 0 dazuaddiert wird (wg. Python 2):

Code: Alles auswählen

print(45/360) # 0
print(45/360.) # 0.125
a fool with a tool is still a fool, www.magben.de, YouTube
gotridofmyphone
User
Beiträge: 33
Registriert: Mittwoch 15. März 2017, 08:54

Sonntag 30. Dezember 2018, 14:53

Ich benutze Python 3, mittlerweile immer, es sei denn ich habe Lust, mir in den Fuß zu schießen, also nie.

Aber auf Umwegen hast du mich trotzdem auf den richtigen Weg gebracht, danke dafür. Des Rätsels Lösung: die Reihenfolge der Argumente an np.arctan2 muss umgetauscht werden. np.arctan2(cosprobe, sinprobe) – und von geringen Abweichungen abgesehen passts.
Antworten