trouver un décalage horaire entre deux formes d'onde similaires

je dois comparer deux formes d'onde temps-vs-tension. En raison de la particularité des sources de ces formes d'onde, l'une d'elles peut être une version décalée dans le temps de l'autre.

Comment puis-je savoir s'il y a un décalage horaire? et si oui, de combien est-il.

je le fais en Python et je souhaite utiliser les bibliothèques num PY/scipy.

18
demandé sur mtrw 2011-01-14 10:06:25

5 réponses

scipy fournit une fonction de corrélation qui fonctionnera très bien pour une petite entrée et aussi si vous voulez une corrélation non circulaire signifiant que le signal ne s'enroule pas. notez que dans mode='full' , la taille du tableau retourné par le signal.la corrélation est la somme des tailles de signal d'entrée - 1, de sorte que la valeur de argmax est désactivée par (taille du signal -1 = 20) à partir de ce que vous semblez attendre.

from scipy import signal, fftpack
import numpy
a = numpy.array([0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 0, 0, 0])
b = numpy.array([0, 0, 0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1, 0])
numpy.argmax(signal.correlate(a,b)) -> 16
numpy.argmax(signal.correlate(b,a)) -> 24

les deux valeurs différentes correspondent à la question de savoir si le changement est dans a ou b .

si vous voulez une corrélation circulaire et pour une grande taille de signal, vous pouvez utiliser le théorème de transformation de convolution/Fourier avec la mise en garde que la corrélation est très similaire mais pas identique à la convolution.

A = fftpack.fft(a)
B = fftpack.fft(b)
Ar = -A.conjugate()
Br = -B.conjugate()
numpy.argmax(numpy.abs(fftpack.ifft(Ar*B))) -> 4
numpy.argmax(numpy.abs(fftpack.ifft(A*Br))) -> 17

encore une fois, les deux valeurs correspondent à votre interprétation d'un décalage dans a ou d'un décalage dans b .

la conjugaison négative est due à la convolution retournement d'une des fonctions, mais en corrélation il n'y a pas de retournement. Vous pouvez annuler le retournement en inversant un des signaux et en prenant ensuite le FFT, ou en prenant le FFT du signal et en prenant ensuite le conjugué négatif. c'est à dire ce qui suit est vrai: Ar = -A.conjugate() = fft(a[::-1])

32
répondu Gus 2011-01-14 10:47:53

Si l'on est décalés dans le temps par l'autre, vous verrez un pic de corrélation. Comme le calcul de la corrélation est coûteux, il est préférable d'utiliser FFT. Ainsi, quelque chose comme ceci devrait fonctionner:

af = scipy.fft(a)
bf = scipy.fft(b)
c = scipy.ifft(af * scipy.conj(bf))

time_shift = argmax(abs(c))
10
répondu highBandWidth 2011-01-14 17:07:12

cette fonction est probablement plus efficace pour les signaux à valeur réelle. Il utilise rfft et Zero pads les entrées à une puissance de 2 assez grande pour assurer une corrélation linéaire (c'est-à-dire non circulaire):

def rfft_xcorr(x, y):
    M = len(x) + len(y) - 1
    N = 2 ** int(np.ceil(np.log2(M)))
    X = np.fft.rfft(x, N)
    Y = np.fft.rfft(y, N)
    cxy = np.fft.irfft(X * np.conj(Y))
    cxy = np.hstack((cxy[:len(x)], cxy[N-len(y)+1:]))
    return cxy

La valeur de retour est la longueur M = len(x) + len(y) - 1 (bidouillé avec hstack pour supprimer les zéros supplémentaires de l'arrondissement jusqu'à une puissance de 2). Les décalages non négatifs sont cxy[0], cxy[1], ..., cxy[len(x)-1] , tandis que les décalages négatifs sont cxy[-1], cxy[-2], ..., cxy[-len(y)+1] .

pour correspondre à signal de référence, je calcule rfft_xcorr(x, ref) et cherche le pic. Par exemple:

def match(x, ref):
    cxy = rfft_xcorr(x, ref)
    index = np.argmax(cxy)
    if index < len(x):
        return index
    else: # negative lag
        return index - len(cxy)   

In [1]: ref = np.array([1,2,3,4,5])
In [2]: x = np.hstack(([2,-3,9], 1.5 * ref, [0,3,8]))
In [3]: match(x, ref)
Out[3]: 3
In [4]: x = np.hstack((1.5 * ref, [0,3,8], [2,-3,-9]))
In [5]: match(x, ref)
Out[5]: 0
In [6]: x = np.hstack((1.5 * ref[1:], [0,3,8], [2,-3,-9,1]))
In [7]: match(x, ref)
Out[7]: -1

ce n'est pas une façon robuste de faire correspondre les signaux, mais c'est rapide et facile.

6
répondu eryksun 2011-01-14 21:15:02

Cela dépend du type de signal que vous avez (périodique?...), à savoir si les deux signaux ont la même amplitude, et sur la précision que vous recherchez.

la fonction de corrélation mentionnée par highBandWidth pourrait en effet fonctionner pour vous. Il est assez simple que vous devriez lui donner un essai.

une autre option, plus précise, est celle que j'utilise pour l'ajustage spectral de haute précision: vous modélisez votre signal "master" avec une spline et vous ajustez le signal décalé dans le temps avec lui (tout en pouvant éventuellement mettre à l'échelle le signal, si besoin est). Cela permet des décalages temporels très précis. Un avantage de cette approche est que vous n'avez pas à étudier la fonction de corrélation. Vous pouvez par exemple créer la spline facilement avec interpolate.UnivariateSpline() (de SciPy). SciPy renvoie une fonction, qui est alors facilement équipée de optimize.leastsq ().

2
répondu Eric Lebigot 2011-01-14 07:54:41

Voici une autre option:

from scipy import signal, fftpack

def get_max_correlation(original, match):
    z = signal.fftconvolve(original, match[::-1])
    lags = np.arange(z.size) - (match.size - 1)
    return ( lags[np.argmax(np.abs(z))] )
2
répondu FFT 2017-04-26 00:45:05