Oznaczanie sygnału z fotodiody

0

Poszukuję dokładnej i wydajnej metody do automatycznego oznaczania sygnału pochodzącego z fotodiody, na podstawie wartości amplitudy zebranego sygnału. Powiedzmy, że rejestruję z monitora zmiany jasności, które wynikają z wyświetlania na nim czarnych i białych pól o różnym kontraście. Sygnał, który rejestruje fotodioda dla częstotliwości rejestracji 1024 Hz wygląda tak (oś x w sekundach):

3s.jpg

Teraz zależy mi, aby dokładnie w czasie osadzić na sygnale "markery", które będą wskazywać, w którym momencie nastąpiła zmiana jasności monitora. Nie chodzi mi bynajmniej o rysowanie czerwonej kreski na wykresie :) Bardziej zależy mi, aby na podstawie posiadanego sygnału "znaleźć" miejsca w czasie, w których nastąpiła zmiana wyświetlanego obrazu.

2s.jpg

Przykładowy set danych:
data.txt

Eny ajdijas? :)

2

Licz sobie pochodną / przyrost wartości funkcji dla kolejnych par punktów i tam gdzie nagle będzie wyraźny skok tam masz skok ;]
Czyli po prostu abs(f(t)-f(t+h)/h) i jak nagle będzie to duża liczba to masz skok.

0

Dzięki @Shalom za odpowiedź, ale obawiam się, że to nie takie proste. Sygnał z fotodiody nie utrzymuje się na stosunkowo stałym poziomie przy określonej luminancji monitora, ale oscyluje non-stop. Wygląda to tak:

s3.jpg

...czerwonym kwadracikiem jest zaznaczony obszar, który został po powiększeniu przedstawiony poniżej:

s2.png

Siedzę teraz nad metodą wykorzystującą średnie kroczące, ale też mi to nie leży...

0

No dobra, ale z tego wynika że amplituda w pewnej chwili mocno skacze. Tzn masz przez pewien czas np. 60-2000 a nagle robi się z tego 60-3000. Może wystarczy w takim razie pamiętać ostatnią amplitudę (z dokładnościa do pewnej delty) i w ten sposób wykrywać skok? :)

1
  1. zrób filtr dolnoprzepustowy (odrzucasz drobne drgania)
    http://www.ufxmarkets.com/learn/forex-school/advanced/calculating-the-macd
    https://autotradingstrategy.wordpress.com/2009/11/30/zero-lag-exponential-moving-average/
    http://en.wikipedia.org/wiki/Filter_%28signal_processing%29

  2. zamień sygnał amplitudowy na skokowy
    Np:
    jeśli abs(dy/dx) > próg -> wynik = wartość sygnału lub "1".
    inaczej -> 0

Wynik z (2) to właśnie sygnał którego szukasz.
Co do (1) to linki są wg stopnia trudności (najprostszy na początku).

1

No więc tak. Do sygnału z fotodiody zastosowałem filtr dolnoprzepustowy, tj.:

# File name "Filters.py"

import scipy.signal as ss

def filt(sig, sf, cf, btype='higphass'):
    """
    :param sig: signal.
    :param sf: sampling frequency.
    :param cf: cut frequencies - array.
    :param btype: bandpass type.
    :return: bandpassed signal.
    """
    if btype == 'higphass' or btype == 'lowpass':
        b, a = ss.butter(3, Wn=cf/(0.5*sf), btype=btype, analog=0, output='ba')
        return ss.filtfilt(b, a, sig)
    elif btype == 'bandstop' or btype == 'bandpass':
        b, a = ss.butter(3, Wn=(cf[0]/(0.5*sf), cf[1]/(0.5*sf)), btype=btype, analog=0, output='ba')
        return ss.filtfilt(b, a, sig)

I jego użycie dla 40 Hz:

import IBD.ElectricalStimulation.Filters as filt

filtered = filt.filt(signal, 1024, 40, btype='lowpass')
py.plot(time_scale, filtered)

Co dało mi:

f1.png

Po czym wyliczyłem pochodną dla całego sygnału, dla kroku = 1, a całość podniosłem do kwadratu, żeby uwypuklić wartości.

# Derivate signal.
step = 1
accuracy_range = 9
derivative = np.diff(filtered, n=step)
derivative = np.append(derivative, np.zeros(step)) ** 2
derivative[derivative > accuracy_range] = np.max(filtered)
derivative[derivative < accuracy_range] = 0
py.plot(time_scale, derivative)

Otrzymałem w rezultacie coś takiego:

f2.png

Prawie jestem w domu, ale niestety nie udaje mi się "uwzględnić" wszystkich "eventów", tj. wszystkich "istotnych" załamków.

UPDATE
Udało mi się jeszcze zwiększyć dokładność oznaczania, manipulując wartością obcinanej częstotliwości i zmienną accuracy_range. Jednak tak, czy siak niektóre eventy pozostają nieoznaczone :/

# Bandpass signal.
import IBD.ElectricalStimulation.Filters as filt

filtered = filt.filt(signal, 1024, 33, btype='lowpass')
py.plot(time_scale, filtered)

# Derivate signal.
step = 1
accuracy_range = 1
derivative = np.diff(filtered, n=step)
derivative = np.append(derivative, np.zeros(step)) ** 2
derivative[derivative > accuracy_range] = np.max(filtered)
derivative[derivative < accuracy_range] = 0
py.plot(time_scale, derivative)

f3.png

UPDATE 2

Wymyśliłem jeszcze coś takiego:

def walk_on_the_bitch(sig, t, interval=1000):
    """
    :param sig:         Pre-processed signal, ie. filtered.
    :param t:           Threshold.
    :param interval:    Interval between next value check (ms);
                        should be a predictive interval between each event.
    """
    last_value = 0
    interval_flag = True
    interval_iterator = 0
    markers = np.zeros(np.size(sig))
    for i in np.arange(np.size(sig)):
        absolute = np.abs(last_value - sig[i])
        last_value = sig[i]
        if interval_flag:
            if absolute > t:
                markers[i] = np.max(sig)
                interval_flag = False
        else:
            if interval_iterator == interval:
                interval_flag = True
                interval_iterator = 0
            else:
                interval_iterator += 1

    return markers

py.plot(time_scale, walk_on_the_bitch(filtered, 0.02))

z4.png

Działa niby idealnie. Nie podoba mi się jednak ta drabinka ifów. Będę musiał jeszcze jednak nad tym pomyśleć. Dzięki chłopaki za pobudzenie kory :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1