from collections import deque import numpy as np from scipy.optimize import curve_fit from scipy.special import erf from scipy.stats import binned_statistic from scipy.signal import hilbert, chirp, find_peaks, savgol_filter from scipy import ndimage from collections import OrderedDict from cam_server.pipeline.data_processing import functions from logging import getLogger import numpy as np initialized = False p0 = None def initialize(parameters, sig): global initialized, buffer buffer = deque(maxlen=parameters["dark_buffer_length"]) buffer.append(np.ones(np.shape(sig))) buffer.append(np.ones(np.shape(sig))) initialized = True def do_fft(t, tr, lm=False, lx=False, padding=False, bins=None): if bins is not None: xbin = np.linspace(0,t[-1]+(t[1]-t[0])/2,bins+1) tr, _, _ = binned_statistic(t, tr, statistic='mean', bins=xbin, range=None) t = np.linspace(0,t[-1],bins) if (lm and lx): lm = np.argmin(abs(t-lm)); lx = np.argmin(abs(t-lx)) tr=tr[lm:lx] t=t[lm:lx] if padding: N = t.size te = t[1]-t[0] t = np.arange(N+pad)*te+t[0] trp = np.zeros(N+pad) trp[:N]=tr tr=trp N = t.size T = t[N-1]-t[0] te = t[1]-t[0] fe = 1.0/te tfd=np.fft.fft(tr)/N freq=np.arange(N)*1.0/T tfd = tfd[:int(len(tfd)/2)] freq = freq[:int(len(freq)/2)] return freq, np.absolute(tfd), np.real(tfd), np.imag(tfd) def gauss(x, x0, fwhm, a): return a*np.exp(-(x-x0)**2/fwhm**2*4*np.log(2)) def gauss_cos( x, x0, fwhm, a, f, phase): return a*np.exp(-(x-x0)**2/fwhm**2*4*np.log(2))*np.cos(2*np.pi*f*(x-x0)-phase) def gauss_sin(x, x0, fwhm, a, f, phase): return a*np.exp(-(x-x0)**2/fwhm**2*4*np.log(2))*np.sin(2*np.pi*f*(x-x0)+phase) def fit_data(x, y, bins=None, diag=False, sigma=None, distance=None): bounds=[[x[abs(y).argmax()]-0.3, 0.03, 0.005, 15., -np.inf], [x[abs(y).argmax()]+0.3, 2., 10., 35., np.inf]] global p0 if p0 is None or np.any([~(lb0] idx_closest = np.argmin(np.abs(xp_pos-x0)) weights=abs(y)[xp[:-1]]+abs(y)[xp[1:]] f = 1/(dt*np.average(np.diff(xp), weights=weights)*2) #f = 1/(dt*np.mean(np.diff(xp_pos[idx_closest-2:idx_closest+1]))) works but uses only two points #f = 1/(dt*np.mean(np.diff(xp_pos))) <-- rather noisy phase = f*(np.min(xp_pos[xp_pos>x0]-x0)*dt)*2*np.pi x0 = x0*dt fwhm = fwhm*dt return x0, fwhm, a, f, phase def normstep(step): """normalizing a test signal for np.correlate""" step = step - np.mean(step) step = step / np.sum(step**2) return step def get_reference_function(sigma_px=30, reflen=150): rng = reflen / np.sqrt(2) / sigma_px / 2 ref = -erf(np.linspace(-rng, rng, reflen)) / 2 ref = normstep(ref) return ref def process(bsdata, pid, timestanp, parameters): global initialized global p0 ## initialize all values sent to BS stream to None x=None sig=None bg=None time=None ratio=None freq=None fft_frequency = None fft_amplitude = None fft_amplitude_real = None fft_amplitude_imag = None x0=None fwhm=None a=None f=None phase=None c=None ## params used for evaluation calibration = parameters["calibration"] padding = parameters["padding"] lm = parameters["time_min"] lx = parameters["time_max"] ## get data and events for sorting events = bsdata[parameters["events"]] sig = bsdata[parameters["sig"]] x = np.arange(0,len(sig)) if not initialized: initialize(parameters, sig) is_midir_dark = parameters["is_midir_dark"] is_midir_dark_evt = False # add to dark reference if is_midir_dark: is_midir_dark_evt = events[parameters["event_code_midir_dark"]] if is_midir_dark_evt: buffer.append(sig) # or analyze bg = np.mean(buffer, axis=0) if not is_midir_dark_evt: ratio = sig/bg time = np.polyval(calibration, x) #fft_frequency, fft_amplitude, fft_amplitude_real, fft_amplitude_imag = do_fft(time, ratio-1, lm=False, lx=False, padding=False, bins=parameters["fft_bins"]) ref=get_reference_function(parameters["sigma_px"], parameters["reflen"]) c = np.correlate(ratio-1, ref, "same") if parameters["fit_data"]: x0, fwhm, a, f, phase = fit_data(time, c, bins=parameters["bins"], distance=parameters["distance"]) else: x0, fwhm, a, f, phase = find_signal(time, c, distance=parameters["distance"]) data = { "SARES20-CEP02:spectrometer_x": x, "SARES20-CEP02:spectrometer_signal": sig, "SARES20-CEP02:spectrometer_background": bg, "SARES20-CEP02:spectrometer_time": time, "SARES20-CEP02:spectrometer_ratio": ratio, "SARES20-CEP02:spectrometer_correlation": c, "SARES20-CEP02:fft_frequency": fft_frequency, "SARES20-CEP02:fft_amplitude_abs": fft_amplitude, "SARES20-CEP02:fft_amplitude_real": fft_amplitude_real, "SARES20-CEP02:fft_amplitude_imag": fft_amplitude_imag, "SARES20-CEP02:fit_x0": x0, "SARES20-CEP02:fit_fwhm": fwhm, "SARES20-CEP02:fit_amplitude": a, "SARES20-CEP01:fit_frequency": f, "SARES20-CEP01:fit_phase": phase, } #data.update({"p_est": pinit, "popt": popt}) return data