Có được giá trị cực đại của tín hiệu nếu tần số nằm giữa hai tâm bin


12

Hãy giả sử như sau:

  • Tần số cơ bản của tín hiệu đã được ước tính bằng FFT và một số phương pháp ước tính tần số và nằm giữa hai trung tâm bin
  • Tần suất lấy mẫu là cố định
  • Nỗ lực tính toán không phải là một vấn đề

Biết tần số, cách chính xác nhất để ước tính giá trị đỉnh tương ứng của các tín hiệu cơ bản là gì?

Một cách có thể là không đệm tín hiệu thời gian để tăng độ phân giải FFT sao cho tâm bin sẽ gần với tần số ước tính hơn. Trong kịch bản này, một điểm tôi không chắc chắn là liệu tôi có thể zero-pad nhiều như tôi muốn hay không nếu có một số hạn chế khi làm như vậy. Một cái khác là trung tâm bin mà tôi nên chọn sau khi đệm bằng 0 là giá trị tôi nhận được giá trị cực đại từ (vì người ta có thể không đạt được tần suất quan tâm chính xác, ngay cả sau khi đệm không).

Tuy nhiên, tôi cũng tự hỏi liệu có một phương pháp nào khác có thể mang lại kết quả tốt hơn không, đó là một công cụ ước tính sử dụng các giá trị đỉnh của hai trung tâm bin xung quanh để ước tính giá trị đỉnh theo tần suất quan tâm.


2
không đệm trước FFT là một cách. Một cách khác là áp dụng một chức năng cửa sổ phù hợp với đầu của bạn. Cửa sổ trên cùng bằng phẳng được thiết kế cho mục đích chính xác này. Tất nhiên, nếu bạn đã biết chính xác tần số và bạn chỉ quan tâm đến một bộ khuếch đại, có lẽ có nhiều cách để làm điều đó rẻ hơn so với FFT.
sellibitze

1
không cần đệm không: nội suy parabol đơn giản (với 3 điểm: imax-1, imax, imax + 1, trong đó imaxđỉnh FFT) sẽ cho bạn kết quả chính xác
Basj

Hãy chắc chắn rằng hàm nội suy khớp với chức năng của cửa sổ. Mặt phẳng là tầm thường, nếu không, bạn muốn có một cặp phù hợp (ví dụ: cửa sổ hình chữ nhật + nội suy chân thành, cửa sổ gaussian + nội suy gaussian, v.v.)
finnw

@CedronDawg câu hỏi này và câu trả lời của nó có liên quan (nhưng không giống nhau) với công thức tần số chính xác của bạn. Có thể bạn có thể tìm thấy nó thú vị.
Fat32

Câu trả lời:


5

Thuật toán đầu tiên nảy ra trong đầu là Thuật toán Goertzel . Thuật toán đó thường giả định rằng tần suất quan tâm là bội số nguyên của tần số cơ bản. Tuy nhiên, bài viết này áp dụng thuật toán (tổng quát) cho trường hợp bạn quan tâm.


Một vấn đề khác là mô hình tín hiệu không chính xác. Nó sử dụng 2*%pi*(1:siglen)*(Fc/siglen). Nó nên sử dụng 2*%pi*(0:siglen-1)*(Fc/siglen)cho pha đi ra chính xác.

Tôi cũng nghĩ rằng có một vấn đề với tần số Fc=21.3rất thấp. Các tín hiệu có giá trị thực tần số thấp có xu hướng biểu hiện sai lệch khi gặp các vấn đề ước tính pha / tần số.

Tôi cũng đã thử tìm kiếm lưới thô cho ước tính pha và nó cho câu trả lời giống như thuật toán Goertzel.

Dưới đây là một biểu đồ cho thấy độ lệch trong cả hai ước tính (Goertzel: xanh lam, Coough: đỏ) cho hai tần số khác nhau: Fc=21.3(solid) và Fc=210.3(nét đứt). Như bạn có thể thấy độ lệch cho tần số cao hơn là ít hơn nhiều.

Biểu đồ -axis là pha ban đầu thay đổi từ 0 đến 2 π .x2π

nhập mô tả hình ảnh ở đây


Chỉ cần kiểm tra mã cho thuật toán Goerzel dựa trên giấy. Sử dụng giá trị DTFT đầu ra, cực đại có thể thu được rất chính xác. Tuy nhiên, có một hệ số tỷ lệ chính xác là 1000. Vì vậy, nếu đỉnh ban đầu là 1,234, sau Goerzel sẽ là 1234. Có ai biết điều này có thể đến từ đâu không?
lR8n6i

Đã làm một số nghiên cứu trong thời gian đó. Có lẽ nó phải làm với tỷ lệ biên độ: tỷ lệ biên độ miền thời gian = hệ số miền tần số * 2 / N, trong đó N là độ dài của tín hiệu. Là giả định này phải không?
lR8n6i


Chào! Tôi chỉ phát hiện ra rằng sử dụng thuật toán Goertzel, biên độ ở hệ số phức kết quả là rất chính xác, nhưng pha hoàn toàn sai. Có ai đó có một ý tưởng nơi này có thể đến từ đâu? Theo "pha" Tôi có nghĩa là độ trễ pha được chỉ định trong cơ bản của tín hiệu gốc.
lR8n6i

1
tội(ω0t+φ)j2[e-jφδ~(ω+ω0+2πk)-e+jφδ~(ω-ω0+2πk)]π/2

4

Nếu bạn sẵn sàng sử dụng nhiều thùng FFT lân cận, không chỉ 2, thì cửa sổ Nội suy giữa các kết quả bin phức tạp có thể tạo ra ước tính rất chính xác, tùy thuộc vào độ rộng của cửa sổ.

Nội suy Windowed Sinc thường được tìm thấy trong các bộ tăng âm thanh chất lượng cao, do đó, các bài viết về chủ đề đó sẽ có các công thức nội suy phù hợp với phân tích lỗi.


Cảm ơn các bình luận. Tôi cũng sẽ thử cách tiếp cận này.
lR8n6i

4

tội(πx)(πx)

[1] JL Flanagan và RM Golden, bộ phát âm Giai đoạn, Tạp chí Kỹ thuật của Bell Bell Systems, tập. 45, trang 14931501509, 1966.

[2] K. Dressler, chiết xuất hình sin bằng cách sử dụng một triển khai hiệu quả của một FFT đa độ phân giải, vì trong Proc. Ngày 9 Conf. trên Hiệu ứng âm thanh kỹ thuật số (DAFx-06), Montreal, Canada, tháng 9 năm 2006, trang 247.


Chào! Cảm ơn rất nhiều cho tất cả các ý kiến ​​của bạn. Tôi đã mở rộng mã của mình (xem bên dưới) để kết hợp bộ lọc Goertzel với phép nội suy đỉnh parabol để lấy pha. Tuy nhiên, kết quả vẫn không chính xác (+ - 3-4deg). Đây có phải là gần như nó được hoặc có sai lầm trong việc hiểu hoặc mã hóa?
lR8n6i


3

Tôi đã gặp rất nhiều khó khăn với vấn đề chính xác này một vài năm trước đây.

Tôi đã đăng câu hỏi này:

/programming/4633203/extracting-precise-frequencies-from-fft-bins-USE-phase-change-b between-framework

Tôi đã kết thúc việc tính toán từ đầu, và đăng một câu trả lời cho câu hỏi của riêng tôi.

Tôi ngạc nhiên rằng tôi không thể tìm thấy bất kỳ giải trình tương tự nào trên Internet.

Tôi sẽ đăng câu trả lời một lần nữa ở đây; lưu ý rằng mã được thiết kế cho một kịch bản trong đó tôi chồng chéo cửa sổ FFT của mình lên gấp 4 lần.

π


Câu đố này có hai chìa khóa để mở khóa nó.

Biểu đồ 3.3:

nhập mô tả hình ảnh ở đây

Biểu đồ 3,4:

nhập mô tả hình ảnh ở đây

Mã số:

for (int k = 0; k <= fftFrameSize/2; k++) 
{
    // compute magnitude and phase 
    bins[k].mag = 2.*sqrt(fftBins[k].real*fftBins[k].real + fftBins[k].imag*fftBins[k].imag);
    bins[k].phase = atan2(fftBins[k].imag, fftBins[k].real);

    // Compute phase difference Δϕ fo bin[k]
    double deltaPhase;
    {
        double measuredPhaseDiff = bins[k].phase - gLastPhase[k];
        gLastPhase[k] = bins[k].phase;

        // Subtract expected phase difference <-- FIRST KEY
        // Think of a single wave in a 1024 float frame, with osamp = 4
        //   if the first sample catches it at phase = 0, the next will 
        //   catch it at pi/2 ie 1/4 * 2pi
        double binPhaseExpectedDiscrepancy = M_TWOPI * (double)k / (double)osamp;
        deltaPhase = measuredPhaseDiff - binPhaseExpectedDiscrepancy;

        // Wrap delta phase into [-Pi, Pi) interval 
        deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);
    }

    // say sampleRate = 40K samps/sec, fftFrameSize = 1024 samps in FFT giving bin[0] thru bin[512]
    // then bin[1] holds one whole wave in the frame, ie 44 waves in 1s ie 44Hz ie sampleRate / fftFrameSize
    double bin1Freq = (double)sampleRate / (double)fftFrameSize;
    bins[k].idealFreq = (double)k * bin1Freq;

    // Consider Δϕ for bin[k] between hops.
    // write as 2π / m.
    // so after m hops, Δϕ = 2π, ie 1 extra cycle has occurred   <-- SECOND KEY
    double m = M_TWOPI / deltaPhase;

    // so, m hops should have bin[k].idealFreq * t_mHops cycles.  plus this extra 1.
    // 
    // bin[k].idealFreq * t_mHops + 1 cycles in t_mHops seconds 
    //   => bins[k].actualFreq = bin[k].idealFreq + 1 / t_mHops
    double tFrame = fftFrameSize / sampleRate;
    double tHop = tFrame / osamp;
    double t_mHops = m * tHop;

    bins[k].freq = bins[k].idealFreq + 1. / t_mHops;
}

Bạn đang nội suy tần số, trong khi OP biết tần số & muốn nội suy biên độ.
vây

2

Mã trăn này sẽ cho bạn một kết quả rất chính xác (tôi đã sử dụng nó cho rất nhiều nốt nhạc và nhận được sai số nhỏ hơn 0,01% semitone) với phép nội suy parabol (phương pháp được sử dụng bởi McAulay Quatieri, Serra, v.v. kỹ thuật tách)

import matplotlib.pyplot as plt
import numpy as np
from scipy.io.wavfile import read
from scipy.fftpack import fft, ifft
import math

(fs, x) = read('test.wav')
if (len(x.shape) == 2):    # if stereo we keep left channel only
 x = x[:,1]

n=x.size
freq = np.arange(n)*1.0/n*fs 
xfft = abs(fft(x))

imax=np.argmax(xfft)  
p=1.0/2*(xfft[imax-1]/xfft[imax]-xfft[imax+1]/xfft[imax])/(xfft[imax-1]/xfft[imax]-2+xfft[imax+1]/xfft[imax])   # parabolic interpolation 
print 'Frequence detectee avec interpolation parabolique :',(imax+p)*1.0/n*fs, 'Hz'

1
clear all
clc

for phase_orig = 0:pi/18:pi,

%% Specify and generate signal
Amp = 1;                     % Amplitude of signal
Fs = 8000;                   % samples per second
dt = 1/Fs;                   % seconds per sample
Fc = 21.3;                   % Hz
StopTime = 0.25;             % seconds
t = (0:dt:StopTime-dt)';     % seconds

siglen = length(t);
sig = Amp * 1.5 * sin(2*pi*(0:siglen-1)*(Fc/siglen) + phase_orig) + 1.5 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 3) ...
  + 1.5 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 5)+ 0.3 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 7) ...
  + 1.3 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 9)+ 1.4 * Amp * sin(2*pi*(0:siglen-1)*(Fc/siglen) * 11);

%% Estimate the peak value of the signals fundamental using Goertzel algorithm
peak = 0;
indvec = [Fc-1 Fc Fc+1];

% Check the input data
if ~isvector(sig) || isempty(sig)
  error('X must be a nonempty vector')
end

if ~isvector(indvec) || isempty(indvec)
  error('INDVEC must be a nonempty vector')
end
if ~isreal(indvec)
  error('INDVEC must contain real numbers')
end

% forcing x to be column
sig = reshape(sig,siglen,1);

% initialization
no_freq = length(indvec); %number of frequencies to compute
y = zeros(no_freq,1); %memory allocation for the output coefficients

% Computation via second-order system
% loop over the particular frequencies
for cnt_freq = 1:no_freq
  %for a single frequency:
  %a/ precompute the constants
  pik_term = 2*pi*(indvec(cnt_freq))/(siglen);
  cos_pik_term2 = cos(pik_term) * 2;
  cc = exp(-1i*pik_term); % complex constant
  %b/ state variables
  s0 = 0;
  s1 = 0;
  s2 = 0;
  %c/ 'main' loop
  for ind = 1:siglen-1 %number of iterations is (by one) less than the length of signal
    %new state
    s0 = sig(ind) + cos_pik_term2 * s1 - s2;  % (*)
    %shifting the state variables
    s2 = s1;
    s1 = s0;
  end
  %d/ final computations
  s0 = sig(siglen) + cos_pik_term2 * s1 - s2; %correspond to one extra performing of (*)
  y(cnt_freq) = s0 - s1*cc; %resultant complex coefficient

  %complex multiplication substituting the last iterationA
  %and correcting the phase for (potentially) non-integer valued
  %frequencies at the same time
  y(cnt_freq) = y(cnt_freq) * exp(-1i*pik_term*(siglen-1));
end

  % perfom amplitude scaling
  peak = abs(y(2)) * 2 / siglen

% perform parabolic interpolation to get the phase estimate
phase_orig=phase_orig*180/pi
ym1 = angle(unwrap(y(1)));
y0 = angle(unwrap(y(2)));
yp1 = angle(unwrap(y(3)));

p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1)); 
phase = y0 - 0.25*(ym1-yp1)*p;
phase_est = phase * 180/pi + 90;
phase_est = mod(phase_est+180,360)-180
end

Tần số bạn đang xử lý (21.3Hz được lấy mẫu ở 8kHz) rất thấp. Vì đây là các tín hiệu có giá trị thực, chúng sẽ thể hiện sai lệch trong ước lượng pha cho ** bất kỳ tần số ** nào.

Hình ảnh này cho thấy một âm mưu của bias ( phase_est - phase_orig) cho Fc = 210.3;(màu đỏ) so với độ lệch cho Fc = 21.3;. Như bạn có thể thấy, phần bù có ý nghĩa hơn nhiều đối với21.3 trường hợp.

Một lựa chọn khác là giảm tỷ lệ lấy mẫu của bạn. Các đường cong màu xanh lá cây cho thấy sự thiên vị cho Fs = 800thay vì 8000.

nhập mô tả hình ảnh ở đây


1
Cảm ơn các cập nhật! Xem cốt truyện của tôi; Tôi vẫn nghĩ rằng bất kỳ công cụ ước tính pha nào cũng sẽ có sai lệch cho tần số thấp này. Một cách để khắc phục điều đó là sử dụng tần số đã biết (nếu biết!) Để điều chỉnh độ lệch ước tính pha thông qua bảng tra cứu. Nhưng bạn sẽ cần phải cẩn thận: độ lệch sẽ thay đổi theo tần số. Một cách khác để làm điều đó sẽ là giảm tỷ lệ lấy mẫu của bạn.
Peter K.

1
Cảm ơn! Tuy nhiên, nếu bạn đang sử dụng Fs = 8000 Hz và Fc = 210 thay vì 210.3 thì độ lệch thậm chí còn tệ hơn. Bất kỳ ý tưởng nơi này có thể đến từ đâu?
lR8n6i

1
Ơ! Không ý kiến. FWIW, công cụ ước tính Geortzel không có vấn đề : goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;. :-) Sẽ đào thêm một chút. Xem không gian này.
Peter K.

1
Nội suy parabol không làm những gì bạn nghĩ nó đang làm. Cụ thể, nếu bạn thay thế tính toán của mình pbằng p2 = (abs(y(3)) - abs(y(1)))/(2*(2*abs(y(2)) - abs(y(3)) - abs(y(1)))); phase2 = y0 - 0.25*(ym1-yp1)*p2;thì bạn sẽ nhận được NHIỀU câu trả lời tốt hơn --- ngay cả đối với Fc=210. Tôi không chắc chắn rằng phiên bản hiện tại psẽ cung cấp cho bạn bất cứ điều gì hợp lý. Công thức nội suy là để nội suy AMPLITUDE của một parabol, nhưng pnội suy pha đó chỉ là ... lẻ.
Peter K.

1
Tất cả điều đó đều ổn, NGOẠI TRỪ rằng vị trí cao nhất ( p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))) sẽ không chính xác trong một số thời gian nếu bạn đang sử dụng PHASES thay vì biên độ. Điều này là do các pha có thể nhảy xung quanh ranh giới +/- 180 độ. Tất cả những gì cần thiết để sửa nó cho giai đoạn là thay đổi dòng đó thành p2tính toán của tôi ở trên.
Peter K.
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.