Beispiel #1
0
def rmsmap(fbin, spectra=True):
    """
    Computes RMS map in time domain and spectra for each channel of Neuropixel probe

    :param fbin: binary file in spike glx format (will look for attached metatdata)
    :type fbin: str or pathlib.Path
    :param spectra: whether to compute the power spectrum (only need for lfp data)
    :type: bool
    :return: a dictionary with amplitudes in channeltime space, channelfrequency space, time
     and frequency scales
    """
    if not isinstance(fbin, spikeglx.Reader):
        sglx = spikeglx.Reader(fbin)
    rms_win_length_samples = 2**np.ceil(np.log2(sglx.fs * RMS_WIN_LENGTH_SECS))
    # the window generator will generates window indices
    wingen = dsp.WindowGenerator(ns=sglx.ns,
                                 nswin=rms_win_length_samples,
                                 overlap=0)
    # pre-allocate output dictionary of numpy arrays
    win = {
        'TRMS': np.zeros((wingen.nwin, sglx.nc)),
        'nsamples': np.zeros((wingen.nwin, )),
        'fscale': dsp.fscale(WELCH_WIN_LENGTH_SAMPLES,
                             1 / sglx.fs,
                             one_sided=True),
        'tscale': wingen.tscale(fs=sglx.fs)
    }
    win['spectral_density'] = np.zeros((len(win['fscale']), sglx.nc))
    # loop through the whole session
    for first, last in wingen.firstlast:
        D = sglx.read_samples(first_sample=first,
                              last_sample=last)[0].transpose()
        # remove low frequency noise below 1 Hz
        D = dsp.hp(D, 1 / sglx.fs, [0, 1])
        iw = wingen.iw
        win['TRMS'][iw, :] = dsp.rms(D)
        win['nsamples'][iw] = D.shape[1]
        if spectra:
            # the last window may be smaller than what is needed for welch
            if last - first < WELCH_WIN_LENGTH_SAMPLES:
                continue
            # compute a smoothed spectrum using welch method
            _, w = signal.welch(D,
                                fs=sglx.fs,
                                window='hanning',
                                nperseg=WELCH_WIN_LENGTH_SAMPLES,
                                detrend='constant',
                                return_onesided=True,
                                scaling='density',
                                axis=-1)
            win['spectral_density'] += w.T
        # print at least every 20 windows
        if (iw % min(20, max(int(np.floor(wingen.nwin / 75)), 1))) == 0:
            print_progress(iw, wingen.nwin)
    return win
def welchogram(fs, wav, nswin=NS_WIN, overlap=OVERLAP, nperseg=NS_WELCH):
    """
    Computes a spectrogram on a very large audio file.

    :param fs: sampling frequency (Hz)
    :param wav: wav signal (vector or memmap)
    :param nswin: n samples of the sliding window
    :param overlap: n samples of the overlap between windows
    :param nperseg: n samples for the computation of the spectrogram
    :return: tscale, fscale, downsampled_spectrogram
    """
    ns = wav.shape[0]
    window_generator = dsp.WindowGenerator(ns=ns, nswin=nswin, overlap=overlap)
    nwin = window_generator.nwin
    fscale = dsp.fscale(nperseg, 1 / fs, one_sided=True)
    W = np.zeros((nwin, len(fscale)))
    tscale = window_generator.tscale(fs=fs)
    detect = []
    for first, last in window_generator.firstlast:
        # load the current window into memory
        w = np.float64(wav[first:last]) * _get_conversion_factor()
        # detection of ready tones
        a = [d + first for d in _detect_ready_tone(w, fs)]
        if len(a):
            detect += a
        # the last window may not allow a pwelch
        if (last - first) < nperseg:
            continue
        # compute PSD estimate for the current window
        iw = window_generator.iw
        _, W[iw, :] = signal.welch(w,
                                   fs=fs,
                                   window='hanning',
                                   nperseg=nperseg,
                                   axis=-1,
                                   detrend='constant',
                                   return_onesided=True,
                                   scaling='density')
        if (iw % 50) == 0:
            window_generator.print_progress()
    window_generator.print_progress()
    # the onset detection may have duplicates with sliding window, average them and remove
    detect = np.sort(np.array(detect)) / fs
    ind = np.where(np.diff(detect) < 0.1)[0]
    detect[ind] = (detect[ind] + detect[ind + 1]) / 2
    detect = np.delete(detect, ind + 1)
    return tscale, fscale, W, detect
Beispiel #3
0
def spectrum(w, fs, smooth=None, unwrap=True, axis=0, **kwargs):
    """
    Display spectral density of a signal along a given dimension
    spectrum(w, fs)
    :param w: signal
    :param fs: sampling frequency (Hz)
    :param smooth: (None) frequency samples to smooth over
    :param unwrap: (True) unwraps the phase specrum
    :param axis: axis on which to compute the FFT
    :param kwargs: plot arguments to be passed to matplotlib
    :return: matplotlib axes
    """
    axis = 0
    smooth = None
    unwrap = True

    ns = w.shape[axis]
    fscale = dsp.fscale(ns, 1 / fs, one_sided=True)
    W = scipy.fft.rfft(w, axis=axis)
    amp = 20 * np.log10(np.abs(W))
    phi = np.angle(W)

    if unwrap:
        phi = np.unwrap(phi)

    if smooth:
        nf = np.round(smooth / fscale[1] / 2) * 2 + 1
        amp = dsp.smooth.mwa(amp, nf)
        phi = dsp.smooth.mwa(phi, nf)

    fig, ax = plt.subplots(2, 1, sharex=True)
    ax[0].plot(fscale, amp, **kwargs)
    ax[1].plot(fscale, phi, **kwargs)

    ax[0].set_title('Spectral Density (dB rel to amplitude.Hz^-0.5)')
    ax[0].set_ylabel('Amp (dB)')
    ax[1].set_ylabel('Phase (rad)')
    ax[1].set_xlabel('Frequency (Hz)')
    return ax