znajdź przesunięcie czasowe między dwoma podobnymi przebiegami

Muszę porównać dwie przebiegi czasu i napięcia. Ze względu na specyfikę źródeł tych fal, jedna z nich może być wersją przesuniętą w czasie drugiej.

Jak mogę sprawdzić, czy jest przesunięcie czasowe? a jeśli tak, to ile to kosztuje.

Robię to w Pythonie i chcę korzystać z bibliotek numpy / scipy.

Author: mtrw, 2011-01-14

5 answers

Scipy zapewnia funkcję korelacji, która będzie działać dobrze dla małych wejść, a także jeśli chcesz korelacji nie okrężnej, co oznacza, że sygnał nie będzie owijać. zauważ , że w mode='full', rozmiar tablicy zwracanej przez sygnał.korelacja jest sumą wielkości sygnału wejściowego - 1, więc wartość z {[3] } jest wyłączona przez (rozmiar sygnału -1 = 20) od tego, czego się spodziewasz.

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

Dwie różne wartości odpowiadają temu, czy przesunięcie jest w a Czy b.

Jeśli chcesz korelacja kołowa i dla dużych rozmiarów sygnału, można użyć twierdzenia splot/transformata Fouriera z zastrzeżeniem, że korelacja jest bardzo podobna, ale nie identyczna z splotem.

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

Ponownie dwie wartości odpowiadają temu, czy interpretujesz przesunięcie w a czy przesunięcie w b.

Ujemna koniugacja jest spowodowana splotem przerzucającym jedną z funkcji, ale w korelacji nie ma przerzucania. Możesz cofnąć rzut, odwracając jeden z sygnały, a następnie biorąc FFT, lub biorąc FFT sygnału, a następnie biorąc ujemną koniugację. tzn. prawda jest następująca: Ar = -A.conjugate() = fft(a[::-1])

 32
Author: Gus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-14 10:47:53

Jeśli jeden jest przesunięty w czasie przez drugi, zobaczysz szczyt w korelacji. Ponieważ obliczanie korelacji jest kosztowne, lepiej jest użyć FFT. Więc coś takiego powinno działać:

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

time_shift = argmax(abs(c))
 10
Author: highBandWidth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-14 17:07:12

Ta funkcja jest prawdopodobnie bardziej wydajna dla sygnałów o wartości rzeczywistej. Wykorzystuje on rfft i zero pads wejścia do mocy 2 wystarczająco dużej, aby zapewnić korelację liniową (tj. nieokrągłą): {]}

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

Wartością zwracaną jest length M = len(x) + len(y) - 1 (połączoną z {[3] } w celu usunięcia dodatkowych zer z zaokrąglenia do potęgi 2). Nieujemne LGD to cxy[0], cxy[1], ..., cxy[len(x)-1], natomiast LGD ujemne to cxy[-1], cxy[-2], ..., cxy[-len(y)+1].

Aby dopasować Sygnał odniesienia, obliczyłem rfft_xcorr(x, ref) i poszukałem piku. Na przykład:
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
Nie jest to solidny sposób dopasowania sygnałów, ale jest szybki i łatwy.
 6
Author: eryksun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-14 21:15:02

Zależy to od rodzaju posiadanego sygnału (okresowego?... ), czy oba sygnały mają tę samą amplitudę i jakiej precyzji szukasz.

Funkcja korelacji, o której mowa w highBandWidth, może rzeczywiście działać dla Ciebie. Jest to na tyle proste, że powinieneś spróbować.

Inną, bardziej precyzyjną opcją jest ta, której używam do precyzyjnego dopasowania linii widmowej: modelujesz sygnał" master " za pomocą splajnu i dopasowujesz do niego sygnał przesunięty w czasie (podczas gdy prawdopodobnie skalowanie sygnału, w razie potrzeby). Daje to bardzo precyzyjne przesunięcia czasowe. Zaletą tego podejścia jest to, że nie trzeba badać funkcji korelacji. Możesz na przykład łatwo utworzyć splajn za pomocą interpolate.UnivariateSpline() (z SciPy). SciPy zwraca funkcję, która jest następnie łatwo dopasowana do optimize.leastsq ().

 2
Author: Eric Lebigot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-14 07:54:41

Oto inna opcja:

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
Author: FFT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-26 00:45:05