Exemple #1
0
def freq_from_hps(signal, fs):
    """Estimate frequency using harmonic product spectrum

    Low frequency noise piles up and overwhelms the desired peaks
    """
    N = len(signal)
    signal -= mean(signal)  # Remove DC offset

    # Compute Fourier transform of windowed signal
    windowed = signal * kaiser(N, 100)

    # Get spectrum
    X = log(abs(rfft(windowed)))

    # Downsample sum logs of spectra instead of multiplying
    hps = copy(X)
    for h in arange(2, 9): # TODO: choose a smarter upper limit
        dec = decimate(X, h)
        hps[:len(dec)] += dec

    # Find the peak and interpolate to get a more accurate peak
    i_peak = argmax(hps[:len(dec)])
    i_interp = parabolic(hps, i_peak)[0]

    # Convert to equivalent frequency
    return fs * i_interp / N  # Hz
Exemple #2
0
def freq_from_autocorr(signal, fs):
    """Estimate frequency using autocorrelation

    Pros: Best method for finding the true fundamental of any repeating wave,
    even with strong harmonics or completely missing fundamental

    Cons: Not as accurate, doesn't find fundamental for inharmonic things like
    musical instruments, this implementation has trouble with finding the true
    peak

    """
    # Calculate autocorrelation (same thing as convolution, but with one input
    # reversed in time), and throw away the negative lags
    signal -= mean(signal)  # Remove DC offset
    corr = fftconvolve(signal, signal[::-1], mode='full')
    corr = corr[len(corr)/2:]

    # Find the first low point
    d = diff(corr)
    start = find(d > 0)[0]

    # Find the next peak after the low point (other than 0 lag).  This bit is
    # not reliable for long signals, due to the desired peak occurring between
    # samples, and other peaks appearing higher.
    i_peak = argmax(corr[start:]) + start
    i_interp = parabolic(corr, i_peak)[0]

    return fs / i_interp
Exemple #3
0
def freq_from_autocorr(signal, fs):
    """Estimate frequency using autocorrelation

    Pros: Best method for finding the true fundamental of any repeating wave,
    even with strong harmonics or completely missing fundamental

    Cons: Not as accurate, doesn't work for inharmonic things like musical
    instruments, this implementation has trouble with finding the true peak

    """
    # Calculate autocorrelation (same thing as convolution, but with one input
    # reversed in time), and throw away the negative lags
    signal -= mean(signal)  # Remove DC offset
    corr = fftconvolve(signal, signal[::-1], mode='full')
    corr = corr[len(corr)/2:]

    # Find the first low point
    d = diff(corr)
    start = find(d > 0)[0]

    # Find the next peak after the low point (other than 0 lag).  This bit is
    # not reliable for long signals, due to the desired peak occurring between
    # samples, and other peaks appearing higher.
    i_peak = argmax(corr[start:]) + start
    i_interp = parabolic(corr, i_peak)[0]

    return fs / i_interp
Exemple #4
0
def freq_from_hps(signal, fs):
    """Estimate frequency using harmonic product spectrum

    Low frequency noise piles up and overwhelms the desired peaks
    """
    N = len(signal)
    signal -= mean(signal)  # Remove DC offset

    # Compute Fourier transform of windowed signal
    windowed = signal * kaiser(N, 100)

    # Get spectrum
    X = log(abs(rfft(windowed)))

    # Remove mean of spectrum (so sum is not increasingly offset
    # only in overlap region)
    X -= mean(X)

    # Downsample sum logs of spectra instead of multiplying
    hps = copy(X)
    for h in arange(2, 9):  # TODO: choose a smarter upper limit
        h = int(h)  # https://github.com/scipy/scipy/pull/7351
        dec = decimate(X, h, zero_phase=True)
        hps[:len(dec)] += dec

    # Find the peak and interpolate to get a more accurate peak
    i_peak = argmax(hps[:len(dec)])
    i_interp = parabolic(hps, i_peak)[0]

    # Convert to equivalent frequency
    return fs * i_interp / N  # Hz
Exemple #5
0
def THD(signal, sample_rate):
    """Measure the THD for a signal
    
    This function is not yet trustworthy.
    
    Returns the estimated fundamental frequency and the measured THD,
    calculated by finding peaks in the spectrum.
    
    There are two definitions for THD, a power ratio or an amplitude ratio
    When finished, this will list both
    
    """
    # Get rid of DC and window the signal
    signal -= mean(signal) # TODO: Do this in the frequency domain, and take any skirts with it?
    windowed = signal * kaiser(len(signal), 100)
    
    # Find the peak of the frequency spectrum (fundamental frequency)
    f = rfft(windowed)
    i = argmax(abs(f))
    true_i = parabolic(log(abs(f)), i)[0]
    print 'Frequency: %f Hz' % (sample_rate * (true_i / len(windowed)))
    
    print 'fundamental amplitude: %.3f' % abs(f[i])
    
    # Find the values for the first 15 harmonics.  Includes harmonic peaks only, by definition
    # TODO: Should peak-find near each one, not just assume that fundamental was perfectly estimated.
    # Instead of limited to 15, figure out how many fit based on f0 and sampling rate and report this "4 harmonics" and list the strength of each
    for x in range(2, 15):
        print '%.3f' % abs(f[i * x]),
   
    THD = sum([abs(f[i*x]) for x in range(2,15)]) / abs(f[i])
    print '\nTHD: %f%%' % (THD * 100)
    return
Exemple #6
0
def freq_from_hps(signal, fs):
    """Estimate frequency using harmonic product spectrum
    
    Low frequency noise piles up and overwhelms the desired peaks
    """
    N = len(signal)
    signal -= mean(signal)  # Remove DC offset

    # Compute Fourier transform of windowed signal
    windowed = signal * kaiser(N, 100)

    # Get spectrum
    X = log(abs(rfft(windowed, _next_regular(N))))

    # Downsample sum logs of spectra instead of multiplying
    hps = copy(X)
    for h in arange(2, 9):  # TODO: choose a smarter upper limit
        dec = decimate(X, h)
        hps[:len(dec)] += dec

    # Find the peak and interpolate to get a more accurate peak
    i_peak = argmax(hps[:len(dec)])
    i_interp = parabolic(hps, i_peak)[0]

    # Convert to equivalent frequency
    return fs * i_interp / N  # Hz
def freq_from_fft(sig, fs):

    # Compute Fourier transform of windowed signal
    windowed = sig * blackmanharris(len(sig))
    fe = rfft(windowed)

    # Find the peak and interpolate to get a more accurate peak
    i = argmax(abs(fe))  # Just use this for less-accurate, naive version
    true_i = parabolic(log(abs(fe)), i)[0]

    # Convert to equivalent frequency
    return fs * true_i / len(windowed)
def THDN(signal, sample_rate):
    """Measure the THD+N for a signal and print the results

    Prints the estimated fundamental frequency and the measured THD+N.  This is
    calculated from the ratio of the entire signal before and after
    notch-filtering.

    This notch-filters by nulling out the frequency coefficients ±10% of the
    fundamental

    """
    # Get rid of DC and window the signal
    signal -= mean(
        signal
    )  # TODO: Do this in the frequency domain, and take any skirts with it?
    windowed = signal * kaiser(len(signal), 100)
    del signal

    # Zero pad to nearest power of two
    new_len = 2**ceil(log(len(windowed)) / log(2))
    windowed = concatenate((windowed, zeros(new_len - len(windowed))))

    # Measure the total signal before filtering but after windowing
    total_rms = rms_flat(windowed)

    # Find the peak of the frequency spectrum (fundamental frequency)
    f = rfft(windowed)
    i = argmax(abs(f))
    true_i = parabolic(log(abs(f)), i)[0]
    print 'Frequency: %f Hz' % (sample_rate * (true_i / len(windowed)))

    # Filter out fundamental by throwing away values ±10%
    lowermin = true_i - 0.1 * true_i
    uppermin = true_i + 0.1 * true_i
    f[lowermin:uppermin] = 0

    # Transform noise back into the time domain and measure it
    noise = irfft(f)
    THDN = rms_flat(noise) / total_rms

    # TODO: RMS and A-weighting in frequency domain?

    # Apply A-weighting to residual noise (Not normally used for distortion,
    # but used to measure dynamic range with -60 dBFS signal, for instance)
    weighted = A_weight(noise, sample_rate)
    THDNA = rms_flat(weighted) / total_rms

    print "THD+N:      %.4f%% or %.1f dB" % (THDN * 100, 20 * log10(THDN))
    print "A-weighted: %.4f%% or %.1f dB(A)" % (THDNA * 100, 20 * log10(THDNA))
Exemple #9
0
def THDN(signal, sample_rate):
    """Measure the THD+N for a signal and print the results

    Prints the estimated fundamental frequency and the measured THD+N.  This is
    calculated from the ratio of the entire signal before and after
    notch-filtering.

    This notch-filters by nulling out the frequency coefficients ±10% of the
    fundamental

    """
    # Get rid of DC and window the signal
    signal -= mean(signal) # TODO: Do this in the frequency domain, and take any skirts with it?
    windowed = signal * kaiser(len(signal), 100)
    del signal

    # Zero pad to nearest power of two
    new_len = 2**ceil( log(len(windowed)) / log(2) )
    windowed = concatenate((windowed, zeros(new_len - len(windowed))))

    # Measure the total signal before filtering but after windowing
    total_rms = rms_flat(windowed)

    # Find the peak of the frequency spectrum (fundamental frequency)
    f = rfft(windowed)
    i = argmax(abs(f))
    true_i = parabolic(log(abs(f)), i)[0]
    print 'Frequency: %f Hz' % (sample_rate * (true_i / len(windowed)))

    # Filter out fundamental by throwing away values ±10%
    lowermin = true_i - 0.1 * true_i
    uppermin = true_i + 0.1 * true_i
    f[lowermin: uppermin] = 0

    # Transform noise back into the time domain and measure it
    noise = irfft(f)
    THDN = rms_flat(noise) / total_rms

    # TODO: RMS and A-weighting in frequency domain?

    # Apply A-weighting to residual noise (Not normally used for distortion,
    # but used to measure dynamic range with -60 dBFS signal, for instance)
    weighted = A_weight(noise, sample_rate)
    THDNA = rms_flat(weighted) / total_rms

    print "THD+N:      %.4f%% or %.1f dB"    % (THDN  * 100, 20 * log10(THDN))
    print "A-weighted: %.4f%% or %.1f dB(A)" % (THDNA * 100, 20 * log10(THDNA))
Exemple #10
0
def freq_from_fft(signal, fs):
    """Estimate frequency from peak of FFT

    Pros: Accurate, usually even more so than zero crossing counter
    (1000.000004 Hz for 1000 Hz, for instance).  Due to parabolic
    interpolation being a very good fit for windowed log FFT peaks?
    https://ccrma.stanford.edu/~jos/sasp/Quadratic_Interpolation_Spectral_Peaks.html
    Accuracy also increases with signal length

    Cons: Doesn't find the right value if harmonics are stronger than
    fundamental, which is common.

    """
    N = len(signal)

    # Compute Fourier transform of windowed signal
    windowed = signal * kaiser(N, 100)
    f = rfft(windowed)
    # Find the peak and interpolate to get a more accurate peak
    i_peak = argmax(abs(f))  # Just use this value for less-accurate result
    i_interp = parabolic(log(abs(f)), i_peak)[0]

    # Convert to equivalent frequency
    return fs * i_interp / N  # Hz
Exemple #11
0
def freq_from_fft(signal, fs):
    """Estimate frequency from peak of FFT

    Pros: Accurate, usually even more so than zero crossing counter
    (1000.000004 Hz for 1000 Hz, for instance).  Due to parabolic
    interpolation being a very good fit for windowed log FFT peaks?
    https://ccrma.stanford.edu/~jos/sasp/Quadratic_Interpolation_Spectral_Peaks.html
    Accuracy also increases with signal length

    Cons: Doesn't find the right value if harmonics are stronger than
    fundamental, which is common.

    """
    N = len(signal)

    # Compute Fourier transform of windowed signal
    windowed = signal * kaiser(N, 100)
    f = rfft(windowed)
    # Find the peak and interpolate to get a more accurate peak
    i_peak = argmax(abs(f))  # Just use this value for less-accurate result
    i_interp = parabolic(log(abs(f)), i_peak)[0]

    # Convert to equivalent frequency
    return fs * i_interp / N  # Hz