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
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