Example #1
0
 def fun(delta, tau, kind, waveform, start_mf):
     try:
         if kind == 'exp':
             start, baseline, value = integrate.filter(self.data,
                                                       bslen,
                                                       delta_exp=delta,
                                                       tau_exp=tau)
         elif kind == 'ma':
             start, baseline, value = integrate.filter(self.data,
                                                       bslen,
                                                       delta_ma=delta,
                                                       length_ma=tau)
         elif kind == 'mf':
             start, baseline, value = integrate.filter(
                 self.data,
                 bslen,
                 delta_mf=delta,
                 length_mf=tau,
                 waveform_mf=waveform,
                 start_mf=start_mf)
         else:
             raise KeyError(kind)
     except ZeroDivisionError:
         return 0
     corr_value = (baseline - value[:, 0])[~self.ignore]
     snr = single_filter_analysis(corr_value)
     return -snr
Example #2
0
def fingerplot(delta=0, bslen=8000):
    """
    Make a fingerplot with the matched filter.
    
    Parameters
    ----------
    delta : int
        The offset from the trigger minus the waveform length.
    bslen : int
        Number of samples used for baseline computation.
    """
    fig1 = plt.figure('fingersnrmf-fingerplot-1', figsize=[7.27, 5.73])
    fig2 = plt.figure('fingersnrmf-fingerplot-2', figsize=[6.4, 4.8])
    fig1.clf()
    fig2.clf()

    trigger, baseline, value = integrate.filter(data, bslen=bslen, delta_mf=len(waveform) + delta, waveform_mf=waveform)
    value = value[:, 0]
    corr_value = (baseline - value)[~ignore]
    snr = single_filter_analysis(corr_value, fig1, fig2)
    print(f'snr = {snr:.2f}')

    fig1.tight_layout()
    fig2.tight_layout()
    fig1.show()
    fig2.show()
Example #3
0
def make_template(data, ignore=None, length=2000, fig=None):
    """
    Make a template waveform for the matched filter.
    
    Parameters
    ----------
    data : array (nevents, 2, 15001)
        As returned by readwav.readwav().
    ignore : bool array (nevents,), optional
        Flag events to be ignored.
    length : int
        Number of samples of the waveform.
    fig : matplotlib figure, optional
        If given, plot the waveform.
    
    Return
    ------
    waveform : array (length,)
        The waveform. It is normalized to unit sum.
    """
    
    if ignore is None:
        ignore = np.zeros(len(data), bool)

    # Run a moving average filter to find and separate the signals by number of
    # photoelectrons.
    trigger, baseline, value = integrate.filter(data, bslen=8000, length_ma=1470, delta_ma=1530)
    corr_value = baseline - value[:, 0]
    snr, center, width = single_filter_analysis(corr_value[~ignore], return_full=True)
    assert snr > 15
    assert len(center) > 2
    
    # Select the data corresponding to 1 photoelectron and subtract the
    # baseline.
    lower = (center[0] + center[1]) / 2
    upper = (center[1] + center[2]) / 2
    selection = (lower < corr_value) & (corr_value < upper) & ~ignore
    t = int(np.median(trigger))
    data1pe = data[selection, 0, t:t + length] - baseline[selection].reshape(-1, 1)
    
    # Compute the waveform as the median of the signals.
    waveform, bottom, top = np.quantile(data1pe, [0.5, 0.25, 0.75], axis=0)
    # waveform = np.mean(data1pe, axis=0)
    # std = np.std(data1pe, axis=0)
    # bottom = waveform - std
    # top = waveform + std
    
    if fig is not None:
        ax = fig.subplots(1, 1)

        ax.fill_between(np.arange(length), bottom, top, facecolor='lightgray', label='25%-75% quantiles')
        ax.plot(waveform, 'k-', label='median')
        
        ax.grid()
        ax.legend(loc='best')
        ax.set_xlabel('Sample number')
        ax.set_ylabel('ADC value')
        ax.set_title('Template for matched filter (unnormalized)')
    
    return waveform / np.sum(waveform)
Example #4
0
def snrseries(deltamin=-50, deltamax=50, ndelta=101, bslen=8000):
    """
    Compute SNR as a function of the offset from the trigger where the filter
    is evaluated ("delta").
    
    Parameters
    ----------
    deltamin, deltamax, ndelta: int
        The delta values where the SNR is computed is a range of ndelta values
        from deltamin to deltamax. The range is specified relative to the
        number of samples of the waveform, i.e. delta=0 -> delta=len(waveform).
    bslen : int
        The number of samples used for the baseline.
    
    Returns
    -------
    delta : array (ndelta,)
        Values of delta.
    snr : array (ndelta,)
        The SNR for each delta.
    """
    delta = np.rint(np.linspace(deltamin, deltamax, ndelta)) + len(waveform)
    start, baseline, value = integrate.filter(data, bslen=bslen, delta_mf=delta, waveform_mf=waveform)

    snr = np.empty(len(delta))

    for i in tqdm.tqdm(range(len(snr))):
        val = value[:, i]
        corr_value = (baseline - val)[~ignore]
        snr[i] = single_filter_analysis(corr_value)
    
    output = delta, snr
    snrplot(*output)
    return output
Example #5
0
def make_template(data,
                  ignore=None,
                  length=2000,
                  noisecorr=False,
                  fig=None,
                  figcov=None,
                  norm=True):
    """
    Make a template waveform for the matched filter.
    
    Parameters
    ----------
    data : array (nevents, 2, 15001)
        As returned by readwav.readwav().
    ignore : bool array (nevents,), optional
        Flag events to be ignored.
    length : int
        Number of samples of the waveform.
    noisecorr : bool
        If True, optimize the filter for the noise spectrum.
    fig : matplotlib figure, optional
        If given, plot the waveform.
    figcov : matplotlib figure, optional
        If given, plot the covariance matrix of the noise as a bitmap.
    norm : bool
        If True, normalize the output to unit sum, so that applying it behaves
        like a weighted mean.
    
    Return
    ------
    waveform : array (length,)
        The waveform.
    """

    if ignore is None:
        ignore = np.zeros(len(data), bool)

    # Run a moving average filter to find and separate the signals by number of
    # photoelectrons.
    trigger, baseline, value = integrate.filter(data,
                                                bslen=8000,
                                                length_ma=1470,
                                                delta_ma=1530)
    corr_value = baseline - value[:, 0]
    snr, center, width = single_filter_analysis(corr_value[~ignore],
                                                return_full=True)
    assert snr > 15
    assert len(center) > 2

    # Select the data corresponding to 1 photoelectron and subtract the
    # baseline.
    lower = (center[0] + center[1]) / 2
    upper = (center[1] + center[2]) / 2
    selection = (lower < corr_value) & (corr_value < upper) & ~ignore
    t = int(np.median(trigger))
    data1pe = data[selection, 0, t:t + length] - baseline[selection].reshape(
        -1, 1)

    # Compute the waveform as the median of the signals.
    waveform = np.median(data1pe, axis=0)
    # waveform = np.mean(data1pe, axis=0)

    if noisecorr:
        # Select baseline data and compute covariance matrix over a slice with
        # the same length of the template.
        bsend = t - 100
        N = 2 * (length + length % 2)
        databs = data[~ignore, 0, bsend - N:bsend]
        cov = np.cov(databs, rowvar=False)
        cov = toeplitze(cov)
        s = slice(N // 4, N // 4 + length)
        cov = cov[s, s]
        # use cov(fweights=~ignore) to avoid using ~ignore

        # Correct the waveform.
        wnocov = waveform
        waveform = linalg.solve(cov, waveform, assume_a='pos')
        waveform *= linalg.norm(cov) / len(waveform)

    if fig is not None:
        axs = fig.subplots(2, 1)

        ax = axs[0]
        if noisecorr:
            ax.plot(wnocov / np.max(np.abs(wnocov)),
                    label='assuming white noise')
            ax.plot(waveform / np.max(np.abs(waveform)),
                    label='corrected for actual noise',
                    zorder=-1)
        else:
            # std = np.std(data1pe, axis=0)
            # bottom = waveform - std
            # top = waveform + std
            bottom, top = np.quantile(data1pe, [0.25, 0.75], axis=0)
            ax.fill_between(np.arange(length),
                            bottom,
                            top,
                            facecolor='lightgray',
                            label='25%-75% quantiles')
            ax.plot(waveform, 'k-', label='median')
            ax.set_ylabel('ADC value')

        ax.grid()
        ax.legend(loc='best')
        ax.set_xlabel('Sample number')
        ax.set_title('Template for matched filter')

        ax = axs[1]

        f, s = signal.periodogram(wnocov if noisecorr else waveform,
                                  window='hann')
        ax.plot(f[1:], np.sqrt(s[1:]), label='spectrum of template')

        f, ss = signal.periodogram(data1pe, axis=-1)
        s = np.median(ss, axis=0)
        ax.plot(f[1:], np.sqrt(s[1:]), label='spectrum of template sources')

        ax.set_ylabel('Spectral density [GHz$^{-1/2}$]')
        ax.set_xlabel('Frequency [GHz]')
        ax.grid()
        ax.set_yscale('log')
        ax.legend(loc='best')

    if noisecorr and figcov is not None:
        ax = figcov.subplots(1, 1)

        m = np.max(np.abs(cov))
        ax.imshow(cov, vmin=-m, vmax=m, cmap='PiYG')

        ax.set_title('Noise covariance matrix')
        ax.set_xlabel('Sample number [ns]')
        ax.set_ylabel('Sample number [ns]')

    if norm:
        waveform /= np.sum(waveform)
    return waveform
Example #6
0
nphotons = [1, 3, 5]
length = 2000
leftmargin = 100
rep = 1
filename = 'darksidehd/nuvhd_lf_3x_tile57_77K_64V_6VoV_1.wav'

###########################

data = readwav.readwav(filename, mmap=False)
mask = ~readwav.spurious_signals(data)

# Run a moving average filter to find and separate the signals by
# number of photoelectrons.
trigger, baseline, value = integrate.filter(data,
                                            bslen=8000,
                                            length_ma=1470,
                                            delta_ma=1530)
corr_value = baseline - value[:, 0]
snr, center, width = single_filter_analysis.single_filter_analysis(
    corr_value[mask], return_full=True)
assert snr > 15
assert len(center) > 2

# Select the data corresponding to a different number of photoelectrons.
nphotons = np.sort(nphotons)
datanph = []
for nph in nphotons:
    lower = (center[nph - 1] + center[nph]) / 2
    upper = (center[nph] + center[nph + 1]) / 2
    selection = (lower < corr_value) & (corr_value < upper) & mask
    indices = np.flatnonzero(selection)[:rep]
Example #7
0
    def fingerplot(self, tau, delta, kind='ma', bslen=8000):
        """
        Make a fingerplot with a specific filter and print the SNR.
    
        Parameters
        ----------
        tau : int
            Length parameter of the filter.
        delta : int
            Offset from the trigger where the filter is evaluated.
        kind : str
            One of 'ma' = moving average, 'exp' = exponential moving average,
            'mf' = matched filter, 'mfn' = matched filter with noise correction.
        bslen : int
            Number of samples used for the baseline.
    
        Return
        ------
        fig1, fig2 : matplotlib figures
        """

        if kind == 'ma':
            start, baseline, value = integrate.filter(self.data,
                                                      bslen,
                                                      delta_ma=delta,
                                                      length_ma=tau)
        elif kind == 'exp':
            start, baseline, value = integrate.filter(self.data,
                                                      bslen,
                                                      delta_exp=delta,
                                                      tau_exp=tau)
        elif kind in ('mf', 'mfn'):
            w0, offset = self.template.matched_filter_template(
                self.template.template_length, timebase=1, aligned='trigger')
            assert offset == 0, offset
            start_mf = integrate.make_start_mf(w0, tau)
            if kind == 'mfn':
                waveform = make_template.make_template(self.data,
                                                       self.ignore,
                                                       tau + start_mf[0],
                                                       noisecorr=True)
            else:
                waveform = w0
            start, baseline, value = integrate.filter(self.data,
                                                      bslen,
                                                      delta_mf=delta,
                                                      waveform_mf=waveform,
                                                      length_mf=tau,
                                                      start_mf=start_mf)
        else:
            raise KeyError(kind)

        corr_value = (baseline - value[:, 0])[~self.ignore]
        fig1 = plt.figure('fingersnr-fingerplot-1', figsize=[7.27, 5.73])
        fig2 = plt.figure('fingersnr-fingerplot-2', figsize=[6.4, 4.8])
        fig1.clf()
        fig2.clf()
        snr = single_filter_analysis(corr_value, fig1, fig2)
        print(f'snr = {snr:.2f}')
        fig1.tight_layout()
        fig2.tight_layout()
        fig1.show()
        fig2.show()

        return fig1, fig2
Example #8
0
    def snrseries(self,
                  tau=_default_tau,
                  ndelta=_default_ndelta,
                  bslen=8000,
                  plot=True):
        """
        Compute SNR as a function of tau and delta. Make a plot and return the
        results.
    
        Parameters
        ----------
        tau : array (ntau,)
            Length parameter of the filters. 
        ndelta : int
            Number of values of offset from trigger explored in a hardcoded range.
        bslen : int
            The number of samples used for the baseline.
        plot : bool
            If False, do not plot. The plot can be done separately by calling
            snrplot().
    
        Returns
        -------
        tau : array (ntau,)
            Values of the filter scale parameter.
        delta_ma : array (ntau, ndelta)
            Values of the offset for the moving average for each tau.
        delta_exp : array (ntau, ndelta)
            Values of the offset for the exponential moving average for each tau.
        delta_mf : array (ntau, ndelta)
            Values of the offset for the matched filter for each tau.
        waveform : array (max(tau),)
            Template used for the matched filter.
        snr : array (3, ntau, ndelta)
            The SNR for (moving average, exponential moving average, matched
            filter), and for each length parameter (tau) and offset from trigger
            (delta).
        """

        # Generate delta ranges.
        ntau = len(tau)
        tau, delta_ma, delta_exp, delta_mf = self.make_tau_delta(tau,
                                                                 ndelta,
                                                                 flat=True)

        print('make template for matched filter...')
        # w0 = make_template.make_template(self.data, self.ignore, np.max(tau) + 200, noisecorr=False)
        w0, offset = self.template.matched_filter_template(
            self.template.template_length, timebase=1, aligned='trigger')
        assert offset == 0, offset
        start_mf = integrate.make_start_mf(w0, tau)
        # waveform = make_template.make_template(self.data, self.ignore, np.max(tau + start_mf), noisecorr=True)
        waveform = w0

        print('computing filters...')
        start, baseline, vma, vexp, vmf = integrate.filter(
            self.data, bslen, delta_ma, tau, delta_exp, tau, delta_mf,
            waveform, tau, start_mf)

        snr = np.empty((3, len(tau)))

        print('analysing filter output...')
        for i in tqdm.tqdm(range(snr.shape[1])):
            for j, value in enumerate([vma, vexp, vmf]):
                value = value[:, i]
                corr_value = (baseline - value)[~self.ignore]
                snr[j, i] = single_filter_analysis(corr_value)

        # Reshape arrays, make plot and return.
        output = (tau.reshape(ntau, ndelta)[:, 0], )
        for x in [delta_ma, delta_exp, delta_mf]:
            output += (x.reshape(ntau, ndelta), )
        output += (waveform, snr.reshape(-1, ntau, ndelta))
        if plot:
            self.snrplot(*output)
        return output