예제 #1
0
def snr_ref_max_profile(
        tfr_coeff_complex: np.ndarray, energy_mean: float,
        snr_max: float) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Computes the snr energy, snr bits and snr entropy from
    frequency-dependent noise model/profile mean energy and max snr per band

    :param tfr_coeff_complex: Complex coefficients for time-frequency representation. Can be real.
    :param energy_mean: baseline frequency-dependent mean energy.
    :param snr_max: baseline max frequency-dependent linear snr
    :return: three np.ndarrays with snr energy, snr bits and snr entropy respectively
    """
    # Evaluate Log energy entropy (LEE) = log(p) and Shannon Entropy (SE) = -p*log(p)
    # Assumes linear spectral coefficien ts (not power), takes the square
    energy = np.abs(tfr_coeff_complex)**2
    snr_lin = energy / energy_mean
    # Surprisal = log(p)
    snr_bits = 0.5 * utils.log2epsilon(snr_lin + EPSILON)

    # SNR entropy per pixel
    snr_entropy = snr_lin * snr_bits
    # Scale by max
    snr_entropy /= snr_max

    return snr_lin, snr_bits, snr_entropy
예제 #2
0
def stft_from_sig(
    sig_wf: np.ndarray, frequency_sample_rate_hz: float, band_order_Nth: float
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    Librosa STFT is complex FFT grid, not power

    :param sig_wf: array with input signal
    :param frequency_sample_rate_hz: sample rate of frequency in Hz
    :param band_order_Nth: Nth order of constant Q bands
    :return: four numpy ndarrays with STFT, STFT_bits, time_stft_s, frequency_stft_hz
    """

    sig_duration_s = len(sig_wf) / frequency_sample_rate_hz
    _, min_frequency_hz = scales.from_duration(band_order_Nth, sig_duration_s)

    order_Nth, cycles_M, quality_Q, \
    frequency_center, frequency_start, frequency_end = \
        scales.frequency_bands_g2f1(scale_order_input=band_order_Nth,
                                    frequency_low_input=min_frequency_hz,
                                    frequency_sample_rate_input=frequency_sample_rate_hz)

    # Choose the spectral resolution as the key parameter
    frequency_resolution_min_hz = np.min(frequency_end - frequency_start)
    frequency_resolution_max_hz = np.max(frequency_end - frequency_start)
    frequency_resolution_hz_geo = np.sqrt(frequency_resolution_min_hz *
                                          frequency_resolution_max_hz)
    stft_time_duration_s = 1 / frequency_resolution_hz_geo
    stft_points_per_seg = int(frequency_sample_rate_hz * stft_time_duration_s)

    # From CQT
    stft_points_hop, _, _, _, _ = \
        scales.cqt_frequency_bands_g2f1(band_order_Nth,
                                        min_frequency_hz,
                                        frequency_sample_rate_hz,
                                        is_power_2=False)

    print('STFT Duration, NFFT, HOP:', len(sig_wf), stft_points_per_seg,
          stft_points_hop)

    STFT_Scaling = 2 * np.sqrt(np.pi) / stft_points_per_seg
    STFT = librosa.core.stft(sig_wf,
                             n_fft=stft_points_per_seg,
                             hop_length=stft_points_hop,
                             win_length=None,
                             window='hann',
                             center=True,
                             pad_mode='reflect')

    # Must be scaled to match scipy psd
    STFT *= STFT_Scaling
    STFT_bits = utils.log2epsilon(STFT)

    time_stft_s = librosa.times_like(STFT,
                                     sr=frequency_sample_rate_hz,
                                     hop_length=stft_points_hop)
    frequency_stft_hz = librosa.core.fft_frequencies(
        sr=frequency_sample_rate_hz, n_fft=stft_points_per_seg)

    return STFT, STFT_bits, time_stft_s, frequency_stft_hz
예제 #3
0
def fft_welch_from_Sxx_bits(f_center: np.ndarray,
                            Sxx: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
    """
    Compute Welch periodogram from spectrogram

    :param f_center: array of sample frequencies.
    :param Sxx: numpy array with spectogram
    :return: numpy array with frequencies, numpy array with Welch periodogram
    """
    # Estimate Welch periodogram by adding Sxx and dividing by the number of windows
    # Removes zero frequency
    Welch_Sxx = np.average(Sxx, axis=1)
    Welch_Sxx_bits = 0.5 * utils.log2epsilon(Welch_Sxx[1:])
    f_center_nozero = f_center[1:]
    return f_center_nozero, Welch_Sxx_bits
예제 #4
0
def fft_complex_bits(
    sig: np.ndarray, sample_interval_s: float
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    Compute the one-dimensional discrete Fourier Transform in bits

    :param sig: array with input signal
    :param sample_interval_s: sample interval in seconds
    :return: four numpy arrays with fft_frequency, fft_sig, fft_spectral_bits, fft_spectral_phase_radians
    """
    # FFT for sigetic, by the book
    fft_points = len(sig)
    fft_sig = np.fft.fft(sig)
    # returns correct RMS power level
    fft_sig /= fft_points
    fft_frequency = np.fft.fftfreq(fft_points, d=sample_interval_s)
    fft_spectral_bits = utils.log2epsilon(fft_sig)
    fft_spectral_phase_radians = np.angle(fft_sig)
    return fft_frequency, fft_sig, fft_spectral_bits, fft_spectral_phase_radians
예제 #5
0
def fft_real_bits(
    sig: np.ndarray, sample_interval_s: float
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    FFT, real frequencies only, magnitude in bits
    :param sig: array with input signal
    :param sample_interval_s: sample interval in seconds
    :return: four numpy ndarrays with fft_frequency_pos, fft_sig_pos, fft_spectral_power_pos_bits,
        fft_spectral_phase_radians
    """
    # FFT for sigetic, by the book
    fft_points = len(sig)
    fft_sig_pos = np.fft.rfft(sig)
    # returns correct RMS power level sqrt(2) -> 1
    fft_sig_pos /= fft_points
    fft_frequency_pos = np.fft.rfftfreq(fft_points, d=sample_interval_s)
    fft_spectral_power_pos_bits = utils.log2epsilon(2. * np.abs(fft_sig_pos))
    fft_spectral_phase_radians = np.angle(fft_sig_pos)
    return fft_frequency_pos, fft_sig_pos, fft_spectral_power_pos_bits, fft_spectral_phase_radians
예제 #6
0
def snr_mean_max(
    tfr_coeff_complex: np.ndarray
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Computes the snr lin energy, snr in bits, and snr entropy defined in Garces (2020)

    :param tfr_coeff_complex: Complex coefficients for time-frequency representation. Can be real.
    :return: three np.ndarrays with snr lin energy, snr in bits, and snr entropy respectively
    """
    # Evaluate Log energy entropy (LEE) = log(p) and Shannon Entropy (SE) = -p*log(p)
    # Assumes linear spectral coefficien ts (not power), takes the square
    energy = np.abs(tfr_coeff_complex)**2
    energy_mean = np.mean(energy)
    snr_lin = energy / energy_mean
    # Surprisal = log(p)
    snr_bits = 0.5 * utils.log2epsilon(snr_lin + EPSILON)

    # SNR entropy per pixel
    snr_entropy = snr_lin * snr_bits
    # Scale by max
    snr_entropy /= np.max(snr_lin)

    return snr_lin, snr_bits, snr_entropy
예제 #7
0
def cqt_from_sig(
    sig_wf: np.ndarray,
    frequency_sample_rate_hz: float,
    band_order_Nth: float,
    cqt_window: str = 'hann',
    dictionary_type: str = "norm"
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    Compute the constant-Q transform of a signal.

    :param sig_wf: array with input signal
    :param frequency_sample_rate_hz: sample rate of frequency in Hz
    :param band_order_Nth: Nth order of constant Q bands
    :param cqt_window: string, "cqt_gauss" or librosa window specification for the basis filter. Default is 'hann'
    :param dictionary_type: "tone" or "norm". Default is 'norm'
    :return: four numpy ndarrays with CQT, CQT_bits, time_cqt_s, frequency_cqt_hz
    """
    sig_duration_s = len(sig_wf) / frequency_sample_rate_hz
    min_scale_s, min_frequency_hz = scales.from_duration(
        band_order_Nth, sig_duration_s)

    # Match default cwt
    cqt_points_hop_min, frequency_hz_center_min, scale_number_bins, order_Nth, cqt_points_per_seg_max = \
        scales.cqt_frequency_bands_g2f1(band_order_Nth,
                                        min_frequency_hz,
                                        frequency_sample_rate_hz,
                                        is_power_2=False)

    print('CQT Duration, NFFT, HOP:', len(sig_wf), cqt_points_per_seg_max,
          cqt_points_hop_min)
    int_order_N = int(band_order_Nth)
    # CQT is not power
    if cqt_window == "cqt_gauss":
        CQT = librosa.core.cqt(sig_wf,
                               sr=frequency_sample_rate_hz,
                               hop_length=cqt_points_hop_min,
                               fmin=frequency_hz_center_min,
                               n_bins=scale_number_bins,
                               bins_per_octave=int_order_N,
                               tuning=0.0,
                               filter_scale=1,
                               norm=1,
                               sparsity=0.0,
                               window=q_gauss,
                               scale=True,
                               pad_mode='reflect')
    else:
        CQT = librosa.core.cqt(sig_wf,
                               sr=frequency_sample_rate_hz,
                               hop_length=cqt_points_hop_min,
                               fmin=frequency_hz_center_min,
                               n_bins=scale_number_bins,
                               bins_per_octave=int_order_N,
                               tuning=0.0,
                               filter_scale=1,
                               norm=1,
                               sparsity=0.0,
                               window=cqt_window,
                               scale=True,
                               pad_mode='reflect')

    time_cqt_s = librosa.times_like(CQT,
                                    sr=frequency_sample_rate_hz,
                                    hop_length=cqt_points_hop_min)
    frequency_cqt_hz = librosa.core.cqt_frequencies(
        scale_number_bins,
        frequency_hz_center_min,
        bins_per_octave=int_order_N,
        tuning=0.0)
    cqt_multiplier = cqt_scaling(band_order_Nth, frequency_cqt_hz,
                                 frequency_sample_rate_hz, CQT.shape,
                                 dictionary_type)
    CQT *= cqt_multiplier
    CQT_bits = utils.log2epsilon(CQT)

    return CQT, CQT_bits, time_cqt_s, frequency_cqt_hz
예제 #8
0
def cwt_chirp_complex(
    band_order_Nth: float,
    sig_wf: np.ndarray,
    frequency_low_hz: float,
    frequency_sample_rate_hz: float,
    frequency_high_hz: float = scales.Slice.F0,
    cwt_type: str = "fft",
    index_shift: float = 0,
    frequency_ref: float = scales.Slice.F1,
    scale_base: float = scales.Slice.G2,
    dictionary_type: str = "norm"
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    Calculate CWT for chirp

    :param band_order_Nth: Nth order of constant Q bands
    :param sig_wf: array with input signal
    :param frequency_low_hz: lowest frequency in Hz
    :param frequency_sample_rate_hz: sample rate in Hz
    :param frequency_high_hz: highest frequency in Hz
    :param cwt_type: one of "conv", "fft", or "morlet2". Default is "fft"
           Address ghost folding in "fft", compared to "conv"
    :param index_shift: index of shift. Default is 0.0
    :param frequency_ref: reference frequency in Hz. Default is F1
    :param scale_base: G2 or G3. Default is G2
    :param dictionary_type: Canonical unit-norm ("norm") or unit spectrum ("spect"). Default is "norm"
    :return: cwt, cwt_bits, time_s, frequency_cwt_hz
    """

    wavelet_points = len(sig_wf)
    time_s = np.arange(wavelet_points) / frequency_sample_rate_hz

    if cwt_type == "morlet2":
        index_shift = 0

    # Planck frequency is absolute upper limit
    if frequency_high_hz > frequency_sample_rate_hz / 2.:
        frequency_high_hz = frequency_sample_rate_hz / 2.

    order_Nth, cycles_M, quality_Q, _,\
    frequency_cwt_hz_flipped, frequency_start_flipped, frequency_end_flipped = \
        chirp_frequency_bands(scale_order_input=band_order_Nth,
                              frequency_low_input=frequency_low_hz,
                              frequency_sample_rate_input=frequency_sample_rate_hz,
                              frequency_high_input=frequency_high_hz,
                              index_shift=index_shift,
                              frequency_ref=frequency_ref,
                              scale_base=scale_base)

    scale_points = len(frequency_cwt_hz_flipped)

    if cwt_type == "morlet2":
        scale_atom = chirp_scale(cycles_M, frequency_cwt_hz_flipped,
                                 frequency_sample_rate_hz)
        cwt_flipped = signal.cwt(data=sig_wf,
                                 wavelet=signal.morlet2,
                                 widths=scale_atom,
                                 w=cycles_M,
                                 dtype=np.complex128)
    elif cwt_type == "fft":
        sig_fft = np.fft.fft(sig_wf)
        cwt_flipped = np.empty((scale_points, wavelet_points),
                               dtype=np.complex128)
        for ii in range(scale_points):
            atom, _ = chirp_centered_4cwt(
                band_order_Nth=order_Nth,
                sig_or_time=sig_wf,
                scale_frequency_center_hz=frequency_cwt_hz_flipped[ii],
                frequency_sample_rate_hz=frequency_sample_rate_hz,
                index_shift=index_shift,
                scale_base=scale_base,
                dictionary_type=dictionary_type)
            atom_fft = np.fft.fft(atom)
            cwt_raw = np.fft.ifft(sig_fft * np.conj(atom_fft))
            cwt_flipped[ii, :] = np.append(cwt_raw[wavelet_points // 2:],
                                           cwt_raw[0:wavelet_points // 2])

    elif cwt_type == "conv":
        cwt_flipped = np.empty((scale_points, wavelet_points),
                               dtype=np.complex128)
        for ii in range(scale_points):
            atom, _ = chirp_centered_4cwt(
                band_order_Nth=order_Nth,
                sig_or_time=sig_wf,
                scale_frequency_center_hz=frequency_cwt_hz_flipped[ii],
                frequency_sample_rate_hz=frequency_sample_rate_hz,
                index_shift=index_shift,
                scale_base=scale_base,
                dictionary_type=dictionary_type)
            cwt_flipped[ii, :] = signal.convolve(sig_wf,
                                                 np.conj(atom)[::-1],
                                                 mode='same')
    else:
        print("Incorrect cwt_type specification in cwt_chirp_complex")

    # Time scales are increasing, which is the opposite of what is expected for the frequency. Flip.
    frequency_cwt_hz = np.flip(frequency_cwt_hz_flipped)
    cwt = np.flipud(cwt_flipped)
    cwt_bits = utils.log2epsilon(cwt)

    return cwt, cwt_bits, time_s, frequency_cwt_hz
예제 #9
0
        freq_target, freq_low, freq_high, band_order, log_scale_base, override
    ]
    print(freq_params)

    w_target = 2 * np.pi * freq_target
    decay_constant = 1 / (2 * w_target**2)

    wwz = libwwz.wwt(magnitudes=mic_sig,
                     timestamps=mic_sig_epoch_s - mic_sig_epoch_s[0],
                     time_divisions=len(mic_cqt_time_s),
                     freq_params=freq_params,
                     decay_constant=decay_constant,
                     method='octave')

    mic_wwz = wwz[3].T
    mic_wwz_bits = utils.log2epsilon(mic_wwz)
    mic_wwz_snr, mic_wwz_snr_bits, mic_wwz_snr_entropy = entropy.snr_mean_max(
        tfr_coeff_complex=mic_wwz)
    pltq.plot_wf_mesh_mesh_vert(redvox_id=station_id_str,
                                wf_panel_2_sig=mic_sig,
                                wf_panel_2_time=mic_sig_epoch_s,
                                mesh_time=mic_cqt_time_s,
                                mesh_frequency=mic_cqt_frequency_hz,
                                mesh_panel_1_trf=mic_wwz_bits,
                                mesh_panel_1_colormap_scaling="range",
                                mesh_panel_0_tfr=mic_wwz_snr_entropy,
                                wf_panel_2_units="Norm",
                                mesh_panel_1_cbar_units="bits",
                                mesh_panel_0_cbar_units="eSNR bits",
                                figure_title="WWZ for " + EVENT_NAME,
                                frequency_hz_ymin=fmin,