Không thể thực hiện thuật toán Goertzel trong Python


7

Sau một số câu hỏi về stackoverflow , tôi đã cố gắng thực hiện thuật toán Goertzel trong Python. Nhưng nó không hoạt động: https://gist.github.com/4128537

import math

def goertzel(samples, sample_rate, f_start, f_end):
    """
    Implementation of the Goertzel algorithm, useful for calculating individual
    terms of a discrete Fourier transform.
    """
    window_size = len(samples)
    f_step = sample_rate / float(window_size)
    # Calculate which DFT bins we'll have to compute
    k_start = int(math.ceil(f_start / f_step))
    k_end = int(math.floor(f_end / f_step))

    if k_end > window_size - 1: raise ValueError('frequency out of range %s' % k_end)

    # For all the bins between `f_start` and `f_end`, calculate the DFT
    # term
    n_range = range(0, window_size)
    freqs = []
    results = []
    for k in range(k_start, k_end + 1):
        # Bin frequency and coefficients for the computation
        f = k * f_step
        w_real = 2.0 * math.cos(2.0 * math.pi * f)
        w_imag = math.sin(2.0 * math.pi * f)

        # Doing the calculation on the whole sample
        d1, d2 = 0.0, 0.0
        for n in n_range:
            y  = samples[n] + w_real * d1 - d2
            d2, d1 = d1, y

        # Storing results `(real part, imag part, power)`
        results.append((
            0.5 * w_real * d1 - d2, w_imag * d1,
            d2**2 + d1**2 - 2 * w_real * d1 * d2)
        )
        freqs.append(f)
    return freqs, results

if __name__ == '__main__':
    # quick test
    import numpy as np
    import pylab

    t = np.linspace(0, 1, 44100)
    sine_wave = np.sin(2*np.pi*441*t)[:1024]
    freqs, results = goertzel(sine_wave, 44100, 0, 22049)
    print np.array(results)
    pylab.plot(freqs, np.array(results)[:,2])
    pylab.show()

Tôi là người mới bắt đầu trong chủ đề này, vì vậy tôi không biết điều gì có thể sai trong đó. Lời khuyên nào cũng được chào đón.

BIÊN TẬP

Đây là những gì tôi nhận được khi vẽ đồ thị ... như bạn có thể nhận thấy, tần số 440 sẽ xuất hiện là không có:

Đây là những gì tôi nhận được khi thực thi mã

Câu trả lời:


7

Chỉ cần nhìn qua, và từ định nghĩa wikipedia của thuật toán Goertzel , tần số trong trọng lượng cosine phải là tần số chuẩn hóa (như đối với DFT, nhân tiện). Nếu bạn sửa đổi mã của mình như dưới đây, bạn sẽ nhận được đầu ra đúng (cũng lưu ý rằng việc tính toán công suất của bạn đã dẫn đến các quyền hạn tiêu cực -sic-, loại bỏ yếu tố dư thừa 2 cũng giải quyết được vấn đề đó).

Mã sửa đổi:

import math

def goertzel(samples, sample_rate, f_start, f_end):
    """
    Implementation of the Goertzel algorithm, useful for calculating individual
    terms of a discrete Fourier transform.
    """
    window_size = len(samples)
    f_step = sample_rate / float(window_size) # JLD: in Hz
    # Calculate which DFT bins we'll have to compute
    k_start = int(math.ceil(f_start / f_step))
    k_end = int(math.floor(f_end / f_step))

    if k_end > window_size - 1: raise ValueError('frequency out of range %s' % k_end)

    # For all the bins between `f_start` and `f_end`, calculate the DFT
    # term
    n_range = range(0, window_size)
    freqs = []
    results = []
    f_step_normalized = 1./window_size # JLD: step in normalized frequency 
    for k in range(k_start, k_end + 1):
        # Bin frequency and coefficients for the computation
        f = k * f_step_normalized # JLD: here you need the normalized frequency
        w_real = 2.0 * math.cos(2.0 * math.pi * f)
        w_imag = math.sin(2.0 * math.pi * f)

        # Doing the calculation on the whole sample
        d1, d2 = 0.0, 0.0
        for n in n_range:
            y  = samples[n] + w_real * d1 - d2
            d2, d1 = d1, y

        # Storing results `(real part, imag part, power)`
        results.append((
            0.5 * w_real * d1 - d2, w_imag * d1,
            d2**2 + d1**2 - w_real * d1 * d2) # removed factor 2: already in w_real!
        )
        freqs.append(f * sample_rate)
    return freqs, results

if __name__ == '__main__':
    # quick test
    import numpy as np
    import pylab

    ##t = np.linspace(0, 1, 44100)
    # JLD: if you do this, the sampling rate is not exactly 44100 anymore:
    #    linspace includes the boundaries 0 and 1, and there are therefore
    #    44100 samples for a bit more than 1 second...
    sample_rate = 44100 # in Hz
    t = np.arange(sample_rate) / np.double(sample_rate) # in seconds
    # JLD: with 1000 samples, a sine at 441Hz leads to a DFT with only one
    # non-nul component:
    sine_wave = np.sin(2*np.pi*441*t)[:1000]  
    freqs, results = goertzel(sine_wave, sample_rate, 0, 22049)
    print np.array(results)
    pylab.figure()
    pylab.clf()
    pylab.plot(np.array(freqs),
               np.array(results)[:,0]**2 + np.array(results)[:,1]**2,
               marker='x',
               label='computed power')
    pylab.plot(np.array(freqs),
               np.array(results)[:,0]**2 + np.array(results)[:,1]**2,
               linestyle='--',
               marker='o',
               label='returned power')
    pylab.xlim([0,1000])
    pylab.ylabel('Power')
    pylab.xlabel('Frequency (Hz)')
    pylab.legend()
    pylab.show()

Tôi đã không kiểm tra kỹ mã đó và tôi không thể đảm bảo nó không có lỗi, nhưng với ví dụ đơn giản của bạn, nó có vẻ hoạt động tốt. Tôi có được con số sau:

đầu ra của mã python ở trên

Mong rằng sẽ giúp!

Trân trọng!

Jean-Louis!


À! Merci infiniment :) Tôi đã vật lộn với điều đó trong hơn một ngày. Một điều cuối cùng: Tôi muốn hiểu điều này về tần số chuẩn hóa ... bạn có thể giải thích ngắn gọn hoặc chỉ cho tôi một số lời giải thích đơn giản không?
sebpiq

@sebpiq De rien! Có một số gợi ý về tần số và tần số chuẩn hóa trong bài viết DTFT . Nói tóm lại, một cách để liên kết tần số chuẩn hóa và "thực": với DFT, bạn chiếu tín hiệu của mình lênđiểm kinh nghiệmj2πkNn, Ở đâu nlà đồng nhất với mẫukN, freq. đã chuẩn hóa, theo chu kỳ trên mỗi mẫu . Để chofS là tỷ lệ lấy mẫu, sau đó t= =nfSlà thời gian tính bằng giâyf= =kNfStính bằng Hz (= chu kỳ mỗi giây ). Bất kỳ khóa học DSP có thể sẽ giải thích điều này chi tiết hơn.
Jean-louis Durrieu

Ok ... tôi nghĩ rằng tôi hiểu. Tôi nghĩ rằng tôi cần một số bồi dưỡng DSP :) Cảm ơn rất nhiều vì sự giúp đỡ.
sebpiq
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.