def ideal_tfr(iflaws, timestamps=None, n_fbins=None): """ideal_tfr :param iflaws: :param timestamps: :param n_fbins: :type iflaws: :type timestamps: :type n_fbins: :return: :rtype: """ ifrow, ifcol = iflaws.shape timestamps, n_fbins = init_default_args(iflaws[0, :], timestamps=timestamps, n_fbins=n_fbins) tcol = timestamps.shape[0] tfr = np.zeros((n_fbins, tcol)) for icol in xrange(tcol): ti = timestamps[icol] for fi in xrange(ifrow): if np.isnan(iflaws[fi, ti]): tfr[ti, fi] = np.nan else: tfr[int(np.round(iflaws[fi, ti] * 2 * (n_fbins - 1))), icol] = 1 freqs = np.arange(n_fbins, dtype=float) / n_fbins * 0.5 return tfr, timestamps, freqs
def narrow_band(signal, lag=None, n_fbins=None): """Narrow band ambiguity function. :param signal: Signal to be analyzed. :param lag: vector of lag values. :param n_fbins: number of frequency bins :type signal: array-like :type lag: array-like :type n_fbins: int :return: Doppler lag representation :rtype: array-like """ n = signal.shape[0] if lag is None: if n % 2 == 0: tau_start, tau_end = -n / 2 + 1, n / 2 else: tau_start, tau_end = -(n - 1) / 2, (n + 1) / 2 lag = np.arange(tau_start, tau_end) taucol = lag.shape[0] n_fbins = init_default_args(signal, n_fbins=n_fbins)[0] naf = np.zeros((n_fbins, taucol), dtype=complex) for icol in xrange(taucol): taui = lag[icol] t = np.arange(abs(taui), n - abs(taui)) naf[t, icol] = signal[t + taui] * np.conj(signal[t - taui]) naf = np.fft.fft(naf, axis=0) _ix1 = np.arange((n_fbins + (n_fbins % 2)) / 2, n_fbins) _ix2 = np.arange((n_fbins + (n_fbins % 2)) / 2) _xi1 = -(n_fbins - (n_fbins % 2)) / 2 _xi2 = ((n_fbins + (n_fbins % 2)) / 2 - 1) xi = np.arange(_xi1, _xi2 + 1, dtype=float) / n_fbins naf = naf[np.hstack((_ix1, _ix2)), :] return naf, lag, xi
def narrow_band(signal, lag=None, n_fbins=None): """Narrow band ambiguity function. :param signal: Signal to be analyzed. :param lag: vector of lag values. :param n_fbins: number of frequency bins :type signal: array-like :type lag: array-like :type n_fbins: int :return: Doppler lag representation :rtype: array-like """ n = signal.shape[0] if lag is None: if n % 2 == 0: tau_start, tau_end = -n / 2 + 1, n / 2 else: tau_start, tau_end = -(n - 1) / 2, (n + 1) / 2 lag = np.arange(tau_start, tau_end) taucol = lag.shape[0] n_fbins = init_default_args(signal, n_fbins=n_fbins)[0] naf = np.zeros((n_fbins, taucol), dtype=complex) for icol in range(taucol): taui = lag[icol] t = np.arange(abs(taui), n - abs(taui)) naf[t, icol] = signal[t + taui] * np.conj(signal[t - taui]) naf = np.fft.fft(naf, axis=0) _ix1 = np.arange((n_fbins + (n_fbins % 2)) / 2, n_fbins) _ix2 = np.arange((n_fbins + (n_fbins % 2)) / 2) _xi1 = -(n_fbins - (n_fbins % 2)) / 2 _xi2 = ((n_fbins + (n_fbins % 2)) / 2 - 1) xi = np.arange(_xi1, _xi2 + 1, dtype=float) / n_fbins naf = naf[np.hstack((_ix1, _ix2)), :] return naf, lag, xi
def smoothed_pseudo_wigner_ville(signal, timestamps=None, freq_bins=None, twindow=None, fwindow=None): """Smoothed Pseudo Wigner-Ville time-frequency distribution. :param signal: signal to be analyzed :param timestamps: time instants of the signal :param freq_bins: number of frequency bins :param twindow: time smoothing window :param fwindow: frequency smoothing window :type signal: array-like :type timestamps: array-like :type freq_bins: int :type twindow: array-like :type fwindow: array-like :return: Smoothed pseudo Wigner Ville distribution :rtype: array-like """ timestamps, freq_bins = init_default_args(signal, timestamps=timestamps, n_fbins=freq_bins) if fwindow is None: winlength = np.floor(freq_bins / 4.0) winlength = winlength + 1 - np.remainder(winlength, 2) from scipy.signal import hamming fwindow = hamming(int(winlength)) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') if twindow is None: timelength = np.floor(freq_bins / 10.0) timelength += 1 - np.remainder(timelength, 2) from scipy.signal import hamming twindow = hamming(int(timelength)) elif twindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') tfr = np.zeros((freq_bins, timestamps.shape[0]), dtype=complex) lg = (twindow.shape[0] - 1) / 2 lh = (fwindow.shape[0] - 1) / 2 for icol in range(timestamps.shape[0]): ti = timestamps[icol] taumax = min([ti + lg - 1, signal.shape[0] - ti + lg, np.round(freq_bins / 2.0) - 1, lh]) points = np.arange(-min([lg, signal.shape[0] - ti]), min([lg, ti - 1]) + 1) g2 = twindow[lg + points] g2 = g2 / np.sum(g2) tfr[0, icol] = np.sum(g2 * signal[ti - points - 1] * np.conj(signal[ti - points - 1])) for tau in range(int(taumax)): points = np.arange(-min([lg, signal.shape[0] - ti - tau]), min([lg, ti - 1 - tau]) + 1) g2 = twindow[lg + points] g2 = g2 / np.sum(g2) R = np.sum(g2 * signal[ti + tau - points - 1] * np.conj(signal[ti - tau - points - 1])) tfr[1 + tau, icol] = fwindow[lh + tau + 1] * R R = np.sum(g2 * signal[ti - tau - points - 1] * np.conj(signal[ti + tau - points - 1])) tfr[freq_bins - tau - 1, icol] = fwindow[lh - tau + 1] * R tau = np.round(freq_bins / 2.0) if (ti <= signal.shape[0] - tau) and (ti >= tau + 1) and (tau <= lh): points = np.arange(-min([lg, signal.shape[0] - ti - tau]), min([lg, ti - 1 - tau]) + 1) g2 = twindow[lg + 1 + points] g2 = g2 / np.sum(g2) _x = np.sum(g2 * signal[ti + tau - points] * np.conj(signal[ti - tau - points])) _x *= fwindow[lh + tau + 1] _y = np.sum(g2 * signal[ti - tau - points] * np.conj(signal[ti + tau - points])) _y *= fwindow[lh - tau + 1] tfr[tau, icol] = (_x + _y) * 0.5 tfr = np.fft.fft(tfr, axis=0) return np.real(tfr)
def smoothed_pseudo_wigner_ville(signal, timestamps=None, freq_bins=None, twindow=None, fwindow=None): """Smoothed Pseudo Wigner-Ville time-frequency distribution. :param signal: signal to be analyzed :param timestamps: time instants of the signal :param freq_bins: number of frequency bins :param twindow: time smoothing window :param fwindow: frequency smoothing window :type signal: array-like :type timestamps: array-like :type freq_bins: int :type twindow: array-like :type fwindow: array-like :return: Smoothed pseudo Wigner Ville distribution :rtype: array-like """ timestamps, freq_bins = init_default_args(signal, timestamps=timestamps, n_fbins=freq_bins) if fwindow is None: winlength = np.floor(freq_bins / 4.0) winlength = winlength + 1 - np.remainder(winlength, 2) from scipy.signal import hamming fwindow = hamming(int(winlength)) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') if twindow is None: timelength = np.floor(freq_bins / 10.0) timelength += 1 - np.remainder(timelength, 2) from scipy.signal import hamming twindow = hamming(int(timelength)) elif twindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') tfr = np.zeros((freq_bins, timestamps.shape[0]), dtype=complex) lg = (twindow.shape[0] - 1) / 2 lh = (fwindow.shape[0] - 1) / 2 for icol in xrange(timestamps.shape[0]): ti = timestamps[icol] taumax = min([ ti + lg - 1, signal.shape[0] - ti + lg, np.round(freq_bins / 2.0) - 1, lh ]) points = np.arange(-min([lg, signal.shape[0] - ti]), min([lg, ti - 1]) + 1) g2 = twindow[lg + points] g2 = g2 / np.sum(g2) tfr[0, icol] = np.sum(g2 * signal[ti - points - 1] * np.conj(signal[ti - points - 1])) for tau in xrange(int(taumax)): points = np.arange(-min([lg, signal.shape[0] - ti - tau]), min([lg, ti - 1 - tau]) + 1) g2 = twindow[lg + points] g2 = g2 / np.sum(g2) R = np.sum(g2 * signal[ti + tau - points - 1] * np.conj(signal[ti - tau - points - 1])) tfr[1 + tau, icol] = fwindow[lh + tau + 1] * R R = np.sum(g2 * signal[ti - tau - points - 1] * np.conj(signal[ti + tau - points - 1])) tfr[freq_bins - tau - 1, icol] = fwindow[lh - tau + 1] * R tau = np.round(freq_bins / 2.0) if (ti <= signal.shape[0] - tau) and (ti >= tau + 1) and (tau <= lh): points = np.arange(-min([lg, signal.shape[0] - ti - tau]), min([lg, ti - 1 - tau]) + 1) g2 = twindow[lg + 1 + points] g2 = g2 / np.sum(g2) _x = np.sum(g2 * signal[ti + tau - points] * np.conj(signal[ti - tau - points])) _x *= fwindow[lh + tau + 1] _y = np.sum(g2 * signal[ti - tau - points] * np.conj(signal[ti + tau - points])) _y *= fwindow[lh - tau + 1] tfr[tau, icol] = (_x + _y) * 0.5 tfr = np.fft.fft(tfr, axis=0) return np.real(tfr)
def pseudo_margenau_hill(signal, timestamps=None, n_fbins=None, fwindow=None): """pseudo_margenau_hill :param signal: :param timestamps: :param n_fbins: :param fwindow: :type signal: :type timestamps: :type n_fbins: :type fwindow: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) tcol = timestamps.shape[0] if fwindow is None: hlength = np.floor(n_fbins / 4.0) if hlength % 2 == 0: hlength += 1 fwindow = ssig.hamming(hlength) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') lh = (fwindow.shape[0] - 1) / 2 fwindow = fwindow / fwindow[lh] tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) dh = derive_window(fwindow) tfr = np.zeros((n_fbins, tcol), dtype=complex) for icol in range(tcol): ti = timestamps[icol] start = min([np.round(n_fbins / 2.0) - 1, lh, xrow - ti]) end = min([np.round(n_fbins / 2.0) - 1, lh, ti - 1]) tau = np.arange(-start, end + 1) indices = np.remainder(n_fbins + tau, n_fbins) tfr[indices, icol] = fwindow[lh + tau] * signal[ti - 1] * np.conj(signal[ti - tau - 1]) tf2[indices, icol] = dh[lh + tau] * signal[ti - 1] * np.conj(signal[ti - tau - 1]) tfr = np.fft.fft(tfr, axis=0) tf2 = np.fft.fft(tf2, axis=0) tfr = tfr.ravel() tf2 = tf2.ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] *= n_fbins / tfr[no_warn_mask] / (2 * np.pi) tf2[no_warn_mask] = np.round(tf2[no_warn_mask]) tfr = np.real(tfr) tf2 = np.imag(tf2) tfr = tfr.reshape(n_fbins, tcol) tf2 = tf2.reshape(n_fbins, tcol) rtfr = np.zeros((n_fbins, tcol), dtype=complex) threshold = 1.0e-6 * (np.abs(signal) ** 2).mean() for icol in range(tcol): for jcol in range(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: jcolhat = jcol - tf2[jcol, icol] jcolhat = np.remainder(np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) jcolhat += 1 rtfr[jcolhat - 1, icol] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat else: tf2[jcol, icol] = np.inf rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def spectrogram(signal, time_samples=None, n_fbins=None, window=None): """Compute the spectrogram and reassigned spectrogram. :param signal: signal to be analzsed :param time_samples: time instants (default: np.arange(len(signal))) :param n_fbins: number of frequency bins (default: len(signal)) :param window: frequency smoothing window (default: Hamming with \ size=len(signal)/4) :type signal: array-like :type time_samples: array-like :type n_fbins: int :type window: array-like :return: spectrogram, reassigned specstrogram and matrix of reassignment vectors :rtype: tuple(array-like) """ if time_samples is None: time_samples = np.arange(signal.shape[0]) elif np.unique(np.diff(time_samples)).shape[0] > 1: raise ValueError('Time instants must be regularly sampled.') n_fbins = init_default_args(signal, n_fbins=n_fbins)[0] if window is None: wlength = int(np.floor(signal.shape[0] / 4.0)) wlength += 1 - np.remainder(wlength, 2) window = ssig.hamming(wlength) elif window.shape[0] % 2 == 0: raise ValueError('The smoothing window must have an odd length.') tfr = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) tf2 = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) tf3 = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) lh = (window.shape[0] - 1) / 2 th = window * np.arange(-lh, lh + 1) dwin = derive_window(window) for icol in range(time_samples.shape[0]): ti = time_samples[icol] tau = np.arange(-np.min([np.round(n_fbins / 2) - 1, lh, ti]), np.min([np.round(n_fbins / 2) - 1, lh, signal.shape[0] - ti]) + 1) indices = np.remainder(n_fbins + tau, n_fbins) norm_h = np.linalg.norm(window[lh + tau], ord=2) tfr[indices, icol] = signal[ti + tau - 1] * np.conj(window[lh + tau]) / norm_h tf2[indices, icol] = signal[ti + tau - 1] * np.conj(th[lh + tau]) / norm_h tf3[indices, icol] = signal[ti + tau - 1] * np.conj(dwin[lh + tau]) / norm_h tfr = np.fft.fft(tfr, axis=0).ravel() tf2 = np.fft.fft(tf2, axis=0).ravel() tf3 = np.fft.fft(tf3, axis=0).ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] = np.round(np.real(tf2[no_warn_mask] / tfr[no_warn_mask])) tf3[no_warn_mask] = np.round(np.imag(n_fbins * tf3[no_warn_mask] / tfr[no_warn_mask] / (2 * np.pi))) tfr = np.abs(tfr) ** 2 tfr = tfr.reshape(n_fbins, time_samples.shape[0]) tf2 = tf2.reshape(n_fbins, time_samples.shape[0]) tf3 = tf3.reshape(n_fbins, time_samples.shape[0]) tf3 = np.real(tf3) rtfr = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) ix = np.arange(time_samples.min(), time_samples.max() + 1) - 1 threshold = 1e-6 * np.mean(np.abs(signal[ix])**2) for icol in range(time_samples.shape[0]): for jcol in range(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: icolhat = icol + tf2[jcol, icol] icolhat = np.min([np.max([icolhat, 1]), time_samples.shape[0]]) jcolhat = jcol - tf3[jcol, icol] jcolhat = np.remainder(np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) rtfr[int(jcolhat), int(icolhat) - 1] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat + 1j * icolhat else: tf2[jcol, icol] = np.inf rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def smoothed_pseudo_wigner_ville(signal, timestamps=None, n_fbins=None, twindow=None, fwindow=None): """smoothed_pseudo_wigner_ville :param signal: :param timestamps: :param n_fbins: :param twindow: :param fwindow: :type signal: :type timestamps: :type n_fbins: :type twindow: :type fwindow: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) if fwindow is None: hlength = np.floor(n_fbins / 4.0) hlength += 1 - (hlength % 2) fwindow = ssig.hamming(hlength) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing window must have an odd length.') lh = (fwindow.shape[0] - 1) / 2 if twindow is None: glength = np.floor(n_fbins / 4.0) glength += 1 - (glength % 2) twindow = ssig.hamming(glength) elif twindow.shape[0] % 2 == 0: raise ValueError('The smoothing window must have an odd length.') lg = (twindow.shape[0] - 1) / 2 tcol = timestamps.shape[0] deltat = timestamps[1:] - timestamps[:-1] if deltat.min() != deltat.max(): raise ValueError("Time instants must be regularly sampled.") else: dt = deltat.min() tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) tf3 = np.zeros((n_fbins, tcol), dtype=complex) dh = derive_window(fwindow) for icol in range(tcol): ti = timestamps[icol] taumax = min([ti + lg - 1, xrow - ti + lg, np.round(n_fbins / 2.0) - 1, lh]) points = np.arange(-min([lg, xrow - ti]), min([lg, ti - 1]) + 1) g2 = twindow[lg + points] g2 = g2 / g2.sum() tg2 = g2 * points xx = signal[ti - 1 - points] * np.conj(signal[ti - 1 - points]) tfr[0, icol] = (g2 * xx).sum() tf2[0, icol] = (tg2 * xx).sum() tf3[0, icol] = dh[lh + 1] * tfr[0, icol] for tau in range(int(taumax)): points = np.arange(-min([lg, xrow - ti - tau]), min([lg, ti - tau - 1]) + 1) g2 = twindow[lg + points] g2 = g2 / g2.sum() tg2 = g2 * points xx = signal[ti + tau - 1 - points] * np.conj(signal[ti - tau - 1 - points]) tfr[tau, icol] = (g2 * xx).sum() * fwindow[lh + tau] tf2[tau, icol] = fwindow[lh + tau] * (tg2 * xx).sum() tf3[tau, icol] = dh[lh + tau] * (g2 * xx).sum() tfr[n_fbins - tau - 1, icol] = (g2 * np.conj(xx)).sum() * fwindow[lh - tau] tf2[n_fbins - tau - 1, icol] = (tg2 * np.conj(xx)).sum() * fwindow[lh - tau] tf3[n_fbins - tau - 1, icol] = dh[lh - tau] * (g2 * np.conj(xx)).sum() tfr = np.real(np.fft.fft(tfr, axis=0)).ravel() tf2 = np.real(np.fft.fft(tf2, axis=0)).ravel() tf3 = np.imag(np.fft.fft(tf3, axis=0)).ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] = np.round(tf2[no_warn_mask] / tfr[no_warn_mask] / dt) tf3[no_warn_mask] = np.round(n_fbins * tf3[no_warn_mask] / tfr[no_warn_mask] / (2 * np.pi)) tfr, tf2, tf3 = [x.reshape(n_fbins, tcol).astype(complex) for x in (tfr, tf2, tf3)] tf3 = np.real(tf3) rtfr = np.zeros((n_fbins, tcol), dtype=complex) ex = np.mean(np.abs(signal) ** 2) threshold = ex * 1.0e-6 for icol in range(tcol): for jcol in range(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: icolhat = min(max([icol - tf2[jcol, icol], 1]), tcol) jcolhat = jcol - tf3[jcol, icol] jcolhat = (((int(jcolhat) - 1) % n_fbins) + n_fbins) % n_fbins + 1 rtfr[jcol, icol] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat + 1j * icolhat else: tf2[jcol, icol] = np.inf * (1 + 1j) rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def morlet_scalogram(signal, timestamps=None, n_fbins=None, tbp=0.25): """morlet_scalogram :param signal: :param timestamps: :param n_fbins: :param tbp: :type signal: :type timestamps: :type n_fbins: :type tbp: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) k = 0.001 tcol = timestamps.shape[0] deltat = timestamps[1:] - timestamps[:-1] if deltat.min() != deltat.max(): raise ValueError("Time instants must be regularly sampled.") else: dt = deltat.min() tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) M = np.ceil(tbp * n_fbins * np.sqrt(2 * np.log(1 / k))) tau = np.arange(M + int(np.round(n_fbins / 2)) + 1) hstar = np.exp(-(tau / (n_fbins * tbp)) ** 2 / 2.0) * np.exp(-1j * 2 * np.pi * tau / n_fbins) thstar = tau * hstar for m in range(1, int(np.round(n_fbins / 2))): factor = np.sqrt(m / (tbp * n_fbins)) for icol in range(tcol): ti = timestamps[icol] tau_neg = np.arange(1, min([np.ceil(M / m), ti - 1]) + 1).astype(int) tau_pos = np.arange(min([np.ceil(M / m), xrow - ti]) + 1).astype(int) # positive frequencies tfr[m, icol] = np.dot(hstar[m * tau_pos - 1], signal[ti + tau_pos - 1]) tf2[m, icol] = np.dot(thstar[m * tau_pos - 1], signal[ti + tau_pos - 1]) if tau_neg.shape[0] > 0: tfr[m, icol] += np.dot(np.conj(hstar[tau_neg * m]), signal[ti - tau_neg]) tf2[m, icol] -= np.dot(np.conj(thstar[tau_neg * m]), signal[ti - tau_neg]) # negative frequencies tfr[n_fbins - m, icol] = np.dot(np.conj(hstar[tau_pos * m - 1]), signal[ti + tau_pos - 1]) tf2[n_fbins - m, icol] = np.dot(np.conj(thstar[tau_pos * m - 1]), signal[ti + tau_pos - 1]) if tau_neg.shape[0] > 0: tfr[n_fbins - m, icol] += np.dot(hstar[tau_neg * m], signal[ti - tau_neg]) tf2[n_fbins - m, icol] -= np.dot(thstar[tau_neg * m], signal[ti - tau_neg]) tfr[m, :] *= factor tf2[m, :] *= factor / m tfr[n_fbins - m, :] *= factor tf2[n_fbins - m, :] *= factor / m m = int(np.round(n_fbins / 2.0)) factor = np.sqrt(m / (tbp * n_fbins)) for icol in range(tcol): ti = timestamps[icol] tau_neg = np.arange(1, min([np.ceil(M / m), ti - 1]) + 1).astype(int) tau_pos = np.arange(min([np.ceil(M / m), xrow - ti]) + 1).astype(int) tau_pos -= 1 tau_neg -= 1 tfr[m, icol] = np.dot(hstar[m * tau_pos], signal[ti + tau_pos]) tf2[m, icol] = np.dot(thstar[m * tau_pos], signal[ti + tau_pos]) if tau_neg.shape[0] > 0: tfr[m, icol] += np.dot(np.conj(hstar[tau_neg * m]), signal[ti - tau_neg]) tf2[m, icol] -= np.dot(np.conj(thstar[tau_neg * m]), signal[ti - tau_neg]) tfr[m, :] *= factor tf2[m, :] *= factor / m tfr, tf2 = tfr.ravel(), tf2.ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] = tf2[no_warn_mask] / tfr[no_warn_mask] tfr = np.abs(tfr) ** 2 tfr = tfr.reshape(n_fbins, tcol) tf2 = tf2.reshape(n_fbins, tcol).astype(complex) rtfr = np.zeros((n_fbins, tcol), dtype=complex) ex = np.mean(np.abs(signal) ** 2) threshold = ex * 1.0e-6 factor = 2 * np.pi * n_fbins * (tbp ** 2) for icol in range(tcol): for jcol in range(n_fbins): if tfr[jcol, icol] > threshold: icolhat = icol + np.round(np.real(tf2[jcol, icol] / dt)) icolhat = min([max([icolhat, 1]), tcol]) m = np.remainder(jcol + np.round(n_fbins / 2.0) - 2, n_fbins) m -= np.round(n_fbins / 2.0) + 1 jcolhat = jcol + np.round(np.imag((m ** 2) * tf2[jcol, icol] / factor)) jcolhat = np.remainder( np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) + 1 rtfr[jcolhat - 1, icolhat - 1] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat + 1j * icolhat else: tf2[jcol, icol] = np.inf * (1 + 1j) rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def pseudo_wigner_ville(signal, timestamps=None, n_fbins=None, fwindow=None): """pseudo_wigner_ville :param signal: :param timestamps: :param n_fbins: :param fwindow: :type signal: :type timestamps: :type n_fbins: :type fwindow: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) tcol = timestamps.shape[0] if fwindow is None: hlength = np.floor(n_fbins / 4.0) if hlength % 2 == 0: hlength += 1 fwindow = ssig.hamming(hlength) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') lh = (fwindow.shape[0] - 1) / 2 fwindow = fwindow / fwindow[lh] tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) dh = derive_window(fwindow) for icol in range(tcol): ti = timestamps[icol] taumax = min([ti - 1, xrow - ti, np.round(n_fbins / 2.0) - 1, lh]) tau = np.arange(-taumax, taumax + 1) indices = np.remainder(n_fbins + tau, n_fbins) + 1 tfr[indices - 1, icol] = fwindow[lh + tau] * signal[ti + tau - 1] * np.conj(signal[ti - tau - 1]) tf2[indices - 1, icol] = dh[lh + tau] * signal[ti + tau - 1] * np.conj(signal[ti - tau - 1]) tau = np.round(n_fbins / 2) if (ti <= (xrow - tau)) and (ti > (tau + 1)) and (tau <= lh): _x = fwindow[lh + 1 + tau] * signal[ti + tau] * np.conj(signal[ti - tau]) _y = fwindow[lh + 1 - tau] * signal[ti - tau] * np.conj(signal[ti + tau]) tfr[tau + 1, icol] = (_x + _y) * 0.5 _x = dh[lh + 1 + tau] * signal[ti + tau] * np.conj(signal[ti - tau]) _y = dh[lh + 1 - tau] * signal[ti - tau] * np.conj(signal[ti + tau]) tf2[tau + 1, icol] = (_x + _y) * 0.5 tfr = np.real(np.fft.fft(tfr, axis=0)) tf2 = np.imag(np.fft.fft(tf2, axis=0)) tfr = tfr.ravel() tf2 = tf2.ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] *= n_fbins / tfr[no_warn_mask] / (2 * np.pi) tf2[no_warn_mask] = np.round(tf2[no_warn_mask]) tfr = tfr.reshape(n_fbins, tcol) tf2 = tf2.reshape(n_fbins, tcol) rtfr = np.zeros((n_fbins, tcol), dtype=complex) tmin = timestamps.min() tmax = timestamps.max() threshold = 1.0e-6 * (np.abs(signal[tmin:(tmax + 1)]) ** 2).mean() for icol in range(tcol): for jcol in range(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: jcolhat = jcol - tf2[jcol, icol] jcolhat = np.remainder(np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) jcolhat += 1 rtfr[jcolhat - 1, icol] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat else: tf2[jcol, icol] = np.inf rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def spectrogram(signal, time_samples=None, n_fbins=None, window=None): """Compute the spectrogram and reassigned spectrogram. :param signal: signal to be analzsed :param time_samples: time instants (default: np.arange(len(signal))) :param n_fbins: number of frequency bins (default: len(signal)) :param window: frequency smoothing window (default: Hamming with \ size=len(signal)/4) :type signal: array-like :type time_samples: array-like :type n_fbins: int :type window: array-like :return: spectrogram, reassigned specstrogram and matrix of reassignment vectors :rtype: tuple(array-like) """ if time_samples is None: time_samples = np.arange(signal.shape[0]) elif np.unique(np.diff(time_samples)).shape[0] > 1: raise ValueError('Time instants must be regularly sampled.') n_fbins = init_default_args(signal, n_fbins=n_fbins)[0] if window is None: wlength = int(np.floor(signal.shape[0] / 4.0)) wlength += 1 - np.remainder(wlength, 2) window = ssig.hamming(wlength) elif window.shape[0] % 2 == 0: raise ValueError('The smoothing window must have an odd length.') tfr = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) tf2 = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) tf3 = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) lh = (window.shape[0] - 1) / 2 th = window * np.arange(-lh, lh + 1) dwin = derive_window(window) for icol in xrange(time_samples.shape[0]): ti = time_samples[icol] tau = np.arange( -np.min([np.round(n_fbins / 2) - 1, lh, ti]), np.min([np.round(n_fbins / 2) - 1, lh, signal.shape[0] - ti]) + 1) indices = np.remainder(n_fbins + tau, n_fbins) norm_h = np.linalg.norm(window[lh + tau], ord=2) tfr[indices, icol] = signal[ti + tau - 1] * np.conj(window[lh + tau]) / norm_h tf2[indices, icol] = signal[ti + tau - 1] * np.conj(th[lh + tau]) / norm_h tf3[indices, icol] = signal[ti + tau - 1] * np.conj(dwin[lh + tau]) / norm_h tfr = np.fft.fft(tfr, axis=0).ravel() tf2 = np.fft.fft(tf2, axis=0).ravel() tf3 = np.fft.fft(tf3, axis=0).ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] = np.round(np.real(tf2[no_warn_mask] / tfr[no_warn_mask])) tf3[no_warn_mask] = np.round( np.imag(n_fbins * tf3[no_warn_mask] / tfr[no_warn_mask] / (2 * np.pi))) tfr = np.abs(tfr)**2 tfr = tfr.reshape(n_fbins, time_samples.shape[0]) tf2 = tf2.reshape(n_fbins, time_samples.shape[0]) tf3 = tf3.reshape(n_fbins, time_samples.shape[0]) tf3 = np.real(tf3) rtfr = np.zeros((n_fbins, time_samples.shape[0]), dtype=complex) ix = np.arange(time_samples.min(), time_samples.max() + 1) - 1 threshold = 1e-6 * np.mean(np.abs(signal[ix])**2) for icol in xrange(time_samples.shape[0]): for jcol in xrange(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: icolhat = icol + tf2[jcol, icol] icolhat = np.min([np.max([icolhat, 1]), time_samples.shape[0]]) jcolhat = jcol - tf3[jcol, icol] jcolhat = np.remainder( np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) rtfr[int(jcolhat), int(icolhat) - 1] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat + 1j * icolhat else: tf2[jcol, icol] = np.inf rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def smoothed_pseudo_wigner_ville(signal, timestamps=None, n_fbins=None, twindow=None, fwindow=None): """smoothed_pseudo_wigner_ville :param signal: :param timestamps: :param n_fbins: :param twindow: :param fwindow: :type signal: :type timestamps: :type n_fbins: :type twindow: :type fwindow: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) if fwindow is None: hlength = np.floor(n_fbins / 4.0) hlength += 1 - (hlength % 2) fwindow = ssig.hamming(hlength) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing window must have an odd length.') lh = (fwindow.shape[0] - 1) / 2 if twindow is None: glength = np.floor(n_fbins / 4.0) glength += 1 - (glength % 2) twindow = ssig.hamming(glength) elif twindow.shape[0] % 2 == 0: raise ValueError('The smoothing window must have an odd length.') lg = (twindow.shape[0] - 1) / 2 tcol = timestamps.shape[0] deltat = timestamps[1:] - timestamps[:-1] if deltat.min() != deltat.max(): raise ValueError("Time instants must be regularly sampled.") else: dt = deltat.min() tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) tf3 = np.zeros((n_fbins, tcol), dtype=complex) dh = derive_window(fwindow) for icol in xrange(tcol): ti = timestamps[icol] taumax = min( [ti + lg - 1, xrow - ti + lg, np.round(n_fbins / 2.0) - 1, lh]) points = np.arange(-min([lg, xrow - ti]), min([lg, ti - 1]) + 1) g2 = twindow[lg + points] g2 = g2 / g2.sum() tg2 = g2 * points xx = signal[ti - 1 - points] * np.conj(signal[ti - 1 - points]) tfr[0, icol] = (g2 * xx).sum() tf2[0, icol] = (tg2 * xx).sum() tf3[0, icol] = dh[lh + 1] * tfr[0, icol] for tau in xrange(int(taumax)): points = np.arange(-min([lg, xrow - ti - tau]), min([lg, ti - tau - 1]) + 1) g2 = twindow[lg + points] g2 = g2 / g2.sum() tg2 = g2 * points xx = signal[ti + tau - 1 - points] * np.conj( signal[ti - tau - 1 - points]) tfr[tau, icol] = (g2 * xx).sum() * fwindow[lh + tau] tf2[tau, icol] = fwindow[lh + tau] * (tg2 * xx).sum() tf3[tau, icol] = dh[lh + tau] * (g2 * xx).sum() tfr[n_fbins - tau - 1, icol] = (g2 * np.conj(xx)).sum() * fwindow[lh - tau] tf2[n_fbins - tau - 1, icol] = (tg2 * np.conj(xx)).sum() * fwindow[lh - tau] tf3[n_fbins - tau - 1, icol] = dh[lh - tau] * (g2 * np.conj(xx)).sum() tfr = np.real(np.fft.fft(tfr, axis=0)).ravel() tf2 = np.real(np.fft.fft(tf2, axis=0)).ravel() tf3 = np.imag(np.fft.fft(tf3, axis=0)).ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] = np.round(tf2[no_warn_mask] / tfr[no_warn_mask] / dt) tf3[no_warn_mask] = np.round(n_fbins * tf3[no_warn_mask] / tfr[no_warn_mask] / (2 * np.pi)) tfr, tf2, tf3 = [ x.reshape(n_fbins, tcol).astype(complex) for x in (tfr, tf2, tf3) ] tf3 = np.real(tf3) rtfr = np.zeros((n_fbins, tcol), dtype=complex) ex = np.mean(np.abs(signal)**2) threshold = ex * 1.0e-6 for icol in xrange(tcol): for jcol in xrange(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: icolhat = min(max([icol - tf2[jcol, icol], 1]), tcol) jcolhat = jcol - tf3[jcol, icol] jcolhat = (( (int(jcolhat) - 1) % n_fbins) + n_fbins) % n_fbins + 1 rtfr[jcol, icol] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat + 1j * icolhat else: tf2[jcol, icol] = np.inf * (1 + 1j) rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def morlet_scalogram(signal, timestamps=None, n_fbins=None, tbp=0.25): """morlet_scalogram :param signal: :param timestamps: :param n_fbins: :param tbp: :type signal: :type timestamps: :type n_fbins: :type tbp: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) k = 0.001 tcol = timestamps.shape[0] deltat = timestamps[1:] - timestamps[:-1] if deltat.min() != deltat.max(): raise ValueError("Time instants must be regularly sampled.") else: dt = deltat.min() tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) M = np.ceil(tbp * n_fbins * np.sqrt(2 * np.log(1 / k))) tau = np.arange(M + int(np.round(n_fbins / 2)) + 1) hstar = np.exp(-(tau / (n_fbins * tbp))**2 / 2.0) * np.exp( -1j * 2 * np.pi * tau / n_fbins) thstar = tau * hstar for m in xrange(1, int(np.round(n_fbins / 2))): factor = np.sqrt(m / (tbp * n_fbins)) for icol in xrange(tcol): ti = timestamps[icol] tau_neg = np.arange(1, min([np.ceil(M / m), ti - 1]) + 1).astype(int) tau_pos = np.arange(min([np.ceil(M / m), xrow - ti]) + 1).astype(int) # positive frequencies tfr[m, icol] = np.dot(hstar[m * tau_pos - 1], signal[ti + tau_pos - 1]) tf2[m, icol] = np.dot(thstar[m * tau_pos - 1], signal[ti + tau_pos - 1]) if tau_neg.shape[0] > 0: tfr[m, icol] += np.dot(np.conj(hstar[tau_neg * m]), signal[ti - tau_neg]) tf2[m, icol] -= np.dot(np.conj(thstar[tau_neg * m]), signal[ti - tau_neg]) # negative frequencies tfr[n_fbins - m, icol] = np.dot(np.conj(hstar[tau_pos * m - 1]), signal[ti + tau_pos - 1]) tf2[n_fbins - m, icol] = np.dot(np.conj(thstar[tau_pos * m - 1]), signal[ti + tau_pos - 1]) if tau_neg.shape[0] > 0: tfr[n_fbins - m, icol] += np.dot(hstar[tau_neg * m], signal[ti - tau_neg]) tf2[n_fbins - m, icol] -= np.dot(thstar[tau_neg * m], signal[ti - tau_neg]) tfr[m, :] *= factor tf2[m, :] *= factor / m tfr[n_fbins - m, :] *= factor tf2[n_fbins - m, :] *= factor / m m = int(np.round(n_fbins / 2.0)) factor = np.sqrt(m / (tbp * n_fbins)) for icol in xrange(tcol): ti = timestamps[icol] tau_neg = np.arange(1, min([np.ceil(M / m), ti - 1]) + 1).astype(int) tau_pos = np.arange(min([np.ceil(M / m), xrow - ti]) + 1).astype(int) tau_pos -= 1 tau_neg -= 1 tfr[m, icol] = np.dot(hstar[m * tau_pos], signal[ti + tau_pos]) tf2[m, icol] = np.dot(thstar[m * tau_pos], signal[ti + tau_pos]) if tau_neg.shape[0] > 0: tfr[m, icol] += np.dot(np.conj(hstar[tau_neg * m]), signal[ti - tau_neg]) tf2[m, icol] -= np.dot(np.conj(thstar[tau_neg * m]), signal[ti - tau_neg]) tfr[m, :] *= factor tf2[m, :] *= factor / m tfr, tf2 = tfr.ravel(), tf2.ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] = tf2[no_warn_mask] / tfr[no_warn_mask] tfr = np.abs(tfr)**2 tfr = tfr.reshape(n_fbins, tcol) tf2 = tf2.reshape(n_fbins, tcol).astype(complex) rtfr = np.zeros((n_fbins, tcol), dtype=complex) ex = np.mean(np.abs(signal)**2) threshold = ex * 1.0e-6 factor = 2 * np.pi * n_fbins * (tbp**2) for icol in xrange(tcol): for jcol in xrange(n_fbins): if tfr[jcol, icol] > threshold: icolhat = icol + np.round(np.real(tf2[jcol, icol] / dt)) icolhat = min([max([icolhat, 1]), tcol]) m = np.remainder(jcol + np.round(n_fbins / 2.0) - 2, n_fbins) m -= np.round(n_fbins / 2.0) + 1 jcolhat = jcol + np.round( np.imag((m**2) * tf2[jcol, icol] / factor)) jcolhat = np.remainder( np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) + 1 rtfr[jcolhat - 1, icolhat - 1] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat + 1j * icolhat else: tf2[jcol, icol] = np.inf * (1 + 1j) rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def pseudo_page(signal, timestamps=None, n_fbins=None, fwindow=None): """pseudo_page :param signal: :param timestamps: :param n_fbins: :param fwindow: :type signal: :type timestamps: :type n_fbins: :type fwindow: :return: :rtype: """ timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) tcol = timestamps.shape[0] if fwindow is None: hlength = np.floor(n_fbins / 4.0) if hlength % 2 == 0: hlength += 1 fwindow = ssig.hamming(hlength) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') lh = (fwindow.shape[0] - 1) / 2 fwindow = fwindow / fwindow[lh] tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) dh = derive_window(fwindow) for icol in xrange(tcol): tau = np.arange(min([n_fbins - 1, lh, icol - 1]) + 1) indices = np.remainder(n_fbins + tau, n_fbins) + 1 tfr[indices, icol] = fwindow[lh + tau] * signal[icol] * np.conj( signal[icol - tau]) tf2[indices, icol] = dh[lh + tau] * signal[icol] * np.conj(signal[icol - tau]) tf2[0, icol] += signal[icol] * np.conj(signal[icol]) tfr = np.fft.fft(tfr, axis=0) tf2 = np.fft.fft(tf2, axis=0) tfr = tfr.ravel() tf2 = tf2.ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] *= n_fbins / tfr[no_warn_mask] / (2 * np.pi) tf2[no_warn_mask] = np.round(tf2[no_warn_mask]) tfr = np.real(tfr) tf2 = np.imag(tf2) tfr = tfr.reshape(n_fbins, tcol) tf2 = tf2.reshape(n_fbins, tcol) rtfr = np.zeros((n_fbins, tcol), dtype=complex) threshold = 1.0e-6 * (np.abs(signal)**2).mean() for icol in xrange(tcol): for jcol in xrange(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: jcolhat = jcol - tf2[jcol, icol] jcolhat = np.remainder( np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) jcolhat += 1 rtfr[jcolhat - 1, icol] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat else: tf2[jcol, icol] = np.inf rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2
def pseudo_wigner_ville(signal, timestamps=None, n_fbins=None, fwindow=None): """pseudo_wigner_ville :param signal: :param timestamps: :param n_fbins: :param fwindow: :type signal: :type timestamps: :type n_fbins: :type fwindow: :return: :rtype: """ xrow = signal.shape[0] timestamps, n_fbins = init_default_args(signal, timestamps=timestamps, n_fbins=n_fbins) tcol = timestamps.shape[0] if fwindow is None: hlength = np.floor(n_fbins / 4.0) if hlength % 2 == 0: hlength += 1 fwindow = ssig.hamming(hlength) elif fwindow.shape[0] % 2 == 0: raise ValueError('The smoothing fwindow must have an odd length.') lh = (fwindow.shape[0] - 1) / 2 fwindow = fwindow / fwindow[lh] tfr = np.zeros((n_fbins, tcol), dtype=complex) tf2 = np.zeros((n_fbins, tcol), dtype=complex) dh = derive_window(fwindow) for icol in xrange(tcol): ti = timestamps[icol] taumax = min([ti - 1, xrow - ti, np.round(n_fbins / 2.0) - 1, lh]) tau = np.arange(-taumax, taumax + 1) indices = np.remainder(n_fbins + tau, n_fbins) + 1 tfr[indices - 1, icol] = fwindow[lh + tau] * signal[ti + tau - 1] * np.conj( signal[ti - tau - 1]) tf2[indices - 1, icol] = dh[lh + tau] * signal[ti + tau - 1] * np.conj( signal[ti - tau - 1]) tau = np.round(n_fbins / 2) if (ti <= (xrow - tau)) and (ti > (tau + 1)) and (tau <= lh): _x = fwindow[lh + 1 + tau] * signal[ti + tau] * np.conj( signal[ti - tau]) _y = fwindow[lh + 1 - tau] * signal[ti - tau] * np.conj( signal[ti + tau]) tfr[tau + 1, icol] = (_x + _y) * 0.5 _x = dh[lh + 1 + tau] * signal[ti + tau] * np.conj( signal[ti - tau]) _y = dh[lh + 1 - tau] * signal[ti - tau] * np.conj( signal[ti + tau]) tf2[tau + 1, icol] = (_x + _y) * 0.5 tfr = np.real(np.fft.fft(tfr, axis=0)) tf2 = np.imag(np.fft.fft(tf2, axis=0)) tfr = tfr.ravel() tf2 = tf2.ravel() no_warn_mask = tfr != 0 tf2[no_warn_mask] *= n_fbins / tfr[no_warn_mask] / (2 * np.pi) tf2[no_warn_mask] = np.round(tf2[no_warn_mask]) tfr = tfr.reshape(n_fbins, tcol) tf2 = tf2.reshape(n_fbins, tcol) rtfr = np.zeros((n_fbins, tcol), dtype=complex) tmin = timestamps.min() tmax = timestamps.max() threshold = 1.0e-6 * (np.abs(signal[tmin:(tmax + 1)])**2).mean() for icol in xrange(tcol): for jcol in xrange(n_fbins): if np.abs(tfr[jcol, icol]) > threshold: jcolhat = jcol - tf2[jcol, icol] jcolhat = np.remainder( np.remainder(jcolhat - 1, n_fbins) + n_fbins, n_fbins) jcolhat += 1 rtfr[jcolhat - 1, icol] += tfr[jcol, icol] tf2[jcol, icol] = jcolhat else: tf2[jcol, icol] = np.inf rtfr[jcol, icol] += tfr[jcol, icol] return tfr, rtfr, tf2