====== Taktrückgewinnung 1.2 ====== ===== takt_finder ===== Wie der Name schon sagt, soll dieser Code den hinterlegten Takt in unserer Aufnahme in Form von Zeitpunkten ausgeben, der ja durch unsere mitgespielte Takt-Frequenz erkennbar ist. Hierfür wird unsere Aufnahme um unsere Takt-Frequenz gefiltert, in Beträge umgewandelt und die Hillbert-Funktion angewendet. Die Hillbert-Funktion sorgt für eine Konturlinie um die Aufnahme der gefilterten Frequenz. Am Ende erhalten wir eine Funktion der Intensität von der Takt-Frequenz. {{:ss15:phase_1.jpg?direct&200|ursprüngliche Aufnahme}} **⇒** {{:ss15:phase_2.jpg?direct&200|Filterung der Takt-Frequenz}} **⇒** {{:ss15:phase_3.jpg?direct&200|Umwandlung in Beträge}} **⇒** {{:ss15:phase_4.jpg?direct&200|Hillbert-Kurve}} def takt_finder(y): '''liefert Liste mit Indizes der Taktflanken''' takt = 0.05*RATE f_takt = 12600 mind = 0.005 minl = 16 idx = [] b, a = signal.butter(4, [(f_takt-100)*2./RATE, (f_takt+100)*2./RATE], btype="bandpass") # Parameter für Butterworth-Filter yf = signal.lfilter(b,a,y) # Taktfrequenzfilter yh = np.abs(signal.hilbert(y)) # Hilbert-Transformation # [...] Somit können wir feststellen, wann die Takt-Frequenz gespielt wurde oder nicht, da bei Auslassung die Lautstärke gegen Null geht. Nun wollen wir eben diese Zeitpunkte von diesem Wechsel zwischen 'An' und 'Aus' der Takt-Frequenz herauslesen. ==== Unterschiede zur Taktrückgewinnung 1.1 ==== Die Taktrückgewinnung 1.1 gibt nur einen Wert, die Taktverschiebung, zurück. Die Version 1.2 liefert dagegen eine Liste mit den konkreten Indizes an welchen sich Taktgrenzen befinden. Daraus ergibt sich ein großer Vorteil der neuen Methode: Es werden nur die Indizes geliefert, an denen auch wirklich etwas gesendet wurde und damit wird auch nicht versucht Aufnahmeabschnitte ohne Signal zu decodieren. Das kann auch dabei helfen verlorene Bits zu vermeiden, wenn weder eine ''0'' oder ''1'' erkannt wird und dadurch weniger Bits ankommen als gesendet wurden. Diese ganzen Vorteile setzen aber voraus, dass der Erkennungsprozess zuverlässig funktioniert. Nachfolgend ist aufgeführt welche Probleme auftreten können und was wir turn um sie zu vermeiden. ==== 1. Problem: ==== Wie eine Lautstärke-Grenze setzen, bei dessen Über- und Unterschreitung ganz klar ein Wechsel von An zu Aus stattfindet und diese auch immer von Flanken durchlaufen wird? === Variante 1 ==== Dazu muss ein absoluter Wert als Grenze gesetzt werden, der jedoch schwer festzusetzen ist, da immer unterschiedliche Lautstärken verwendet werden könnten. Dazu vereinheitlichen wir die bisher gewonnen Aufnahme (siehe ''ye''). Egal mit welcher Lautstärke der Sender sendet, wir erhalten immer das ungefähr gleiche Bild einer Funktion in ihrem Maximum (ca. 0.0034 bei guten Aufnahmen). Dazu setzen wir eine möglichst kleine Grenze mit einem kleinen Intervall als Bedingung, die nun unsere Takt-Flanken erkennt. {{:ss15:takt_1.jpg?direct&300 |Resultat einer guten Aufnahme}} {{:ss15:takt_2.jpg?direct&300 |keine Beeinflussung durch einzelne Extrema}} ye = (1/(np.linalg.norm(yf)))*yh # Vereinheitlichung der Hillbertkurve für jede Lautstärke idx = np.where(np.abs(ye-0.001)<0.00002) # Grenze 0.001 im Toleranzbereich 0.00002 enthält ca. 3 indices an einer Flanke v.extend(np.array(idx).flatten().tolist()) \\ \\ \\ === Variante 2 === Die Variante 1 wirft allerdings ein Problem auf: Wurde im untersuchten Abschnitt kein Signal aufgenommen, d.h. es herrscht nahezu Stille auf der Taktfrequenz, wird diese Stille mit ihrem ganz geringen Grundrauschen trotzdem auf den üblichen Wert verstärkt. Das führt dann aber dazu, dass Takt-Flanken erkannt werden wo gar keine sind. Deshalb erachte ich es für sinnvoller eine feste minimale Grenze zu definieren, ab dem das Taktsignal als vorhanden gewertet wird und die Grenze zur Erkennung wie gehabt über das Maximum oder ähnlich zu berechnen. # [...] ma = max(yh) if ma > mind: s = ma/5 tol = 0.002 * ma idx = np.where(np.abs(yh-s) ==== 2. Problem: ==== Wie können wir unsere 'richtigen' Takt-Flanken von den 'falschen' Takt-Flanken unterscheiden und herausfiltern? Denn es können bei unschönen Aufnahmen immer Fehler entstehen, die mit unserer einfachen Grenzsetzung einhergehen. Wir haben somit erstmal nur eine Sammlung von sehr vielen potentiellen 'richtigen' Flanken. Zum einen enthält das Intervall um die Grenze mehrere Indices für nur eine gewollte Flanke und zum anderen müssen diese Indices auch in dem vorgegebenen Taktzeitabstand passen! Und dazu wird die Rohliste ''v'' überprüft. Per for-Schleife werden die 'richtigen' Flanken in eine neue Liste übertragen. Der momentane Index wird mit dem nächstkommenden Index auf seinen Abstand überprüft und nur als 'richtig' übertragen, wenn diese im Taktabstand mit einer Toleranz von +-5% vom Taktabstand zueinander stehen. Somit fallen sofort alle Indizes raus, deren Abstand ungleich des Taktabstandes zum nächsten Index ist, also die der gleichen Flanke angehören oder die den 'falschen' Flanken angehören, die sich zwischenmogeln könnten. Da Flanken nur als 'richtig' gelten, wenn es die nächste bestätigt, werden hier 'falsche' Flanken ein Problem. Obwohl eine Flanke 'richtig' wäre, würde sie nicht bestätigt werden können. Dazu gibt es einen Zähler k, der später auf diese versäumte Flanke zurückführt, sobald die nächste 'richtige' Flanke gefunden wurde. So würde die gegenwärtige Flanke in der Liste zur Bestätigung für eine versäumte Flanke werden. {{:ss15:takt_7.jpg?direct&300 |}} {{:ss15:takt_9.jpg?direct&300 |}} \\ \\ \\ \\ \\ \\ \\ \\ \\ ==== 3. Problem: ==== Die letzte Flanke muss ja auch noch erkannt werden, jedoch geht das schlecht ohne eine nachfolgende Flanke nach bisherigem System. Das System wird da einfach umgedreht. Die Liste v wird dabei nun rückwärts durchlaufen bis es zu einer Bestätigung kommt. Hierbei wurde uns klar, dass das letzte Bit natürlich auch von Flanken eingeschlossen werden muss. So mussten wir unsere code-Funktion umschreiben. Vorher endete unsere Takt-Frequenz nämlich mit Aus. Nun mit An. ==== 4. Problem: ==== Trotz aller Takterkennungsmaßnahmen könnten ja sicherlich einfach durch eine schlechte Übertragung/Aufnahme wichtige Flanken fehlen, die dann rekonstruiert werden müssten. Dies geschieht durch nochmalige Überprüfung der Taktabstände aller Indices der Liste h. Lücken werden dabei durch einfaches Addieren des Taktabstandes mit Indices gefüllt. # [...] v=[] # alle Indices um Grenze h=[] # Sortierung der Indices aus v nach nur richtigen Takt-Flanken e=[] # Hilfliste zur Ergänzung von Takt-Flanken, falls richtige Takt-Flanken nicht auffindbar (Lückenliste) n=0 # Zähler für v-Liste m=0 # Zähler für h-Liste k=0 # Zähler für falsche Takt-Flanken in v-Liste v.extend(np.array(idx).flatten().tolist()) for x in v: n+=1 if n==len(v): for x in sorted(v,reverse=True): # v-liste von hinten nochmal durchgehen, damit letzte Takt-Flanke noch in h hinzugefügt wird if h[-1]+takt+0.1*takt>x and h[-1]+takt-0.05*taktv[n] and x+takt-0.05*taktx and v[n-k-1]+takt-0.05*takth[m] and x+takt-0.05*takt ====Resultat:==== Wir erhalten die genauen Zeitpunkte eines Wechsels der Lautstärke der Takt-Frequenz und damit lässt sich die Takteinteilung des Signals ablesen.