Esempio n. 1
0
def THDN(signal, fs, weight=None):
    """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

    TODO: Make R vs F reference a parameter (currently is R)
    TODO: Or report all of the above in a dictionary?

    """
    # Get rid of DC and window the signal
    signal = np.asarray(signal) + 0.0  # Float-like array
    # TODO: Do this in the frequency domain, and take any skirts with it?
    signal -= mean(signal)

    window = general_cosine(len(signal), flattops['HFT248D'])
    windowed = signal * window
    del signal

    # Zero pad to nearest power of two
    new_len = next_fast_len(len(windowed))
    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]
    frequency = fs * (true_i / len(windowed))

    # Filter out fundamental by throwing away values ±10%
    lowermin = int(true_i * 0.9)
    uppermin = int(true_i * 1.1)
    f[lowermin: uppermin] = 0
    # TODO: Zeroing FFT bins is bad

    # Transform noise back into the time domain and measure it
    noise = irfft(f)
    # TODO: RMS and A-weighting in frequency domain?  Parseval?

    if weight is None:
        pass
    elif weight == 'A':
        # Apply A-weighting to residual noise (Not normally used for
        # distortion, but used to measure dynamic range with -60 dBFS signal,
        # for instance)
        noise = A_weight(noise, fs)
        # TODO: filtfilt? tail end of filter?
    else:
        raise ValueError('Weighting not understood')

    # TODO: Return a dict or list of frequency, THD+N?
    return rms_flat(noise) / total_rms
Esempio n. 2
0
def THDN(signal, fs, weight=None):
    """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

    TODO: Make R vs F reference a parameter (currently is R)
    TODO: Or report all of the above in a dictionary?

    """
    # Get rid of DC and window the signal
    signal = np.asarray(signal) + 0.0  # Float-like array
    # TODO: Do this in the frequency domain, and take any skirts with it?
    signal -= mean(signal)

    window = general_cosine(len(signal), flattops['HFT248D'])
    windowed = signal * window
    del signal

    # Zero pad to nearest power of two
    new_len = next_fast_len(len(windowed))
    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]
    frequency = fs * (true_i / len(windowed))

    # Filter out fundamental by throwing away values ±10%
    lowermin = int(true_i * 0.9)
    uppermin = int(true_i * 1.1)
    f[lowermin:uppermin] = 0
    # TODO: Zeroing FFT bins is bad

    # Transform noise back into the time domain and measure it
    noise = irfft(f)
    # TODO: RMS and A-weighting in frequency domain?  Parseval?

    if weight is None:
        pass
    elif weight == 'A':
        # Apply A-weighting to residual noise (Not normally used for
        # distortion, but used to measure dynamic range with -60 dBFS signal,
        # for instance)
        noise = A_weight(noise, fs)
        # TODO: filtfilt? tail end of filter?
    else:
        raise ValueError('Weighting not understood')

    # TODO: Return a dict or list of frequency, THD+N?
    return rms_flat(noise) / total_rms
Esempio n. 3
0
def THD(signal, fs):
    """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.

    TODO: Make weighting a parameter
    TODO: Make R vs F reference a parameter (F as default??)

    """
    # Get rid of DC and window the signal
    signal = np.asarray(signal) + 0.0  # Float-like array
    # TODO: Do this in the frequency domain, and take any skirts with it?
    signal -= mean(signal)

    window = general_cosine(len(signal), flattops['HFT248D'])
    windowed = signal * window
    del signal

    # 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' % (fs * (true_i / len(windowed))))
    print('fundamental amplitude: %.3f' % abs(f[i]))

    harmonics_num = 15

    # 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, harmonics_num):
        print('%.3f' % abs(f[i * x]), end=' ')

    THD = sum([abs(f[i * x]) for x in range(2, harmonics_num)]) / abs(f[i])
    print('\nTHD (up to %d harmonic): %f%%' % (harmonics_num, (THD * 100)))
    return
Esempio n. 4
0
def THD(signal, fs):
    """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.

    TODO: Make weighting a parameter
    TODO: Make R vs F reference a parameter (F as default??)

    """
    # Get rid of DC and window the signal
    signal = np.asarray(signal) + 0.0  # Float-like array
    # TODO: Do this in the frequency domain, and take any skirts with it?
    signal -= mean(signal)

    window = general_cosine(len(signal), flattops['HFT248D'])
    windowed = signal * window
    del signal

    # 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' % (fs * (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]), end=' ')

    THD = sum([abs(f[i*x]) for x in range(2, 15)]) / abs(f[i])
    print('\nTHD: %f%%' % (THD * 100))
    return
Esempio n. 5
0
 def test_basic(self):
     assert_allclose(windows.general_cosine(5, [0.5, 0.3, 0.2]),
                     [0.4, 0.3, 1, 0.3, 0.4])
     assert_allclose(windows.general_cosine(4, [0.5, 0.3, 0.2], sym=False),
                     [0.4, 0.3, 1, 0.3])
Esempio n. 6
0
sig_xmitt = np.sin(2 * pi * fc * tsig)
sig_xmitt *= hann(tsig.size)

# create an interpolator
sig_ier = interp1d(tsig, sig_xmitt, kind=3, bounds_error=False, fill_value=0.)

# create time series as a sum of all arrivals
x_sig = np.zeros_like(taxis)
x_sig += sig_ier(taxis - time_dir) / r_dir
x_sig -= sig_ier(taxis - time_surf) / r_surf
x_sig -= sig_ier(taxis - time_bottom) / r_bottom

# this is really not necassary, but this is a good window

nuttall4c = [0.3635819, 0.4891775, 0.1365995, 0.0106411]
window = general_cosine(winwidth, nuttall4c, sym=False)
num_overlap = int(np.ceil(winwidth * .656))

num_windows = int(np.floor((taxis.size - winwidth) / num_overlap))
win_position = np.arange(num_windows) * num_overlap

sig_FT = []
for wp in win_position:
    sig_FT.append(np.fft.rfft(x_sig[wp: wp + winwidth] * window))

sig_FT = np.array(sig_FT)
win_time = (win_position + winwidth / 2) / fs
win_time += taxis[0]

faxis = np.arange(winwidth // 2 + 1) / winwidth * fs
fbin = int(np.argmin(np.abs(faxis - fc)))
Esempio n. 7
0
 def test_basic(self):
     assert_allclose(windows.general_cosine(5, [0.5, 0.3, 0.2]),
                     [0.4, 0.3, 1, 0.3, 0.4])
     assert_allclose(windows.general_cosine(4, [0.5, 0.3, 0.2], sym=False),
                     [0.4, 0.3, 1, 0.3])
Esempio n. 8
0
# .. math::  z = \frac{2 \pi j}{N}, j = 0...N - 1

# Since this uses the convention of starting at the origin, to reproduce the
# window, we need to convert every other coefficient to a positive number:

HFT90D = [1, 1.942604, 1.340318, 0.440811, 0.043097]

# The paper states that the highest sidelobe is at -90.2 dB.  Reproduce
# Figure 42 by plotting the window and its frequency response, and confirm
# the sidelobe level in red:

from scipy.signal.windows import general_cosine
from scipy.fftpack import fft, fftshift
import matplotlib.pyplot as plt

window = general_cosine(1000, HFT90D, sym=False)
plt.plot(window)
plt.title("HFT90D window")
plt.ylabel("Amplitude")
plt.xlabel("Sample")

plt.figure()
A = fft(window, 10000) / (len(window)/2.0)
freq = np.linspace(-0.5, 0.5, len(A))
response = 20 * np.log10(np.abs(fftshift(A / abs(A).max())))
plt.plot(freq, response)
plt.axis([-50/1000, 50/1000, -140, 0])
plt.title("Frequency response of the HFT90D window")
plt.ylabel("Normalized magnitude [dB]")
plt.xlabel("Normalized frequency [cycles per sample]")
plt.axhline(-90.2, color='red')