주파수가 2 개의 빈 중심 사이에있는 경우 신호의 피크 값을 얻습니다.


12

다음을 가정하십시오.

  • 신호의 기본 주파수는 FFT와 일부 주파수 추정 방법을 사용하여 추정되었으며 두 개의 빈 중심 사이에 있습니다.
  • 샘플링 주파수는 고정되어 있습니다
  • 계산 노력은 문제가되지 않습니다

주파수를 알면 신호의 해당 피크 값을 추정하는 가장 정확한 방법은 무엇입니까?

빈 중심이 추정 된 주파수에 더 가깝도록 FFT 분해능을 높이기 위해 시간 신호를 제로 패드하는 것이 한 가지 방법입니다. 이 시나리오에서 내가 확신하지 못하는 한 가지 점은 원하는만큼 제로 패드를 사용할 수 있는지 또는 그렇게하는 데 몇 가지 단점이 있다는 것입니다. 또 다른 하나는 제로 패딩 후 피크 값을 얻는 것으로 빈 패닝 센터를 선택 해야하는 것입니다 (제로 패딩 후에도 관심 주파수를 정확하게 맞지 않을 수 있기 때문에).

그러나 더 나은 결과를 제공 할 수있는 또 다른 방법이 있는지 궁금합니다. 주변 두 개의 빈 중심의 피크 값을 사용하여 관심 주파수의 피크 값을 추정하는 추정기입니다.


2
FFT 이전의 제로 패딩은 한 가지 방법입니다. 다른 하나는 당신의 neads에 적합한 창 기능을 적용하는 것입니다. 평평한 상단 창은 정확히이 목적을 위해 설계되었습니다. 물론 주파수를 이미 알고 있고 단 하나의 암페어 타이드에 관심이 있다면 FFT보다 더 저렴한 방법이있을 것입니다.
sellibitze

1
제로 패딩 필요 없음 : 간단한 포물선 보간 (3 점 : imax-1, imax, imax + 1, imaxFFT 피크 위치)은 정확한 결과를 제공합니다
Basj

보간 기능이 윈도우 기능과 일치하는지 확인하십시오. Flat-top은 사소한 것이지만 그렇지 않으면 일치하는 쌍을 원합니다 (예 : 직사각형 창 + sinc 보간, 가우시안 창 + 가우시안 보간 등)
finnw

@CedronDawg이 질문과 답변은 정확한 빈도 공식과 관련이 있지만 동일하지는 않습니다. 흥미로울 수 있습니다.
Fat32

답변:


5

떠오르는 첫 번째 알고리즘은 Goertzel Algorithm 입니다. 이 알고리즘은 일반적으로 관심 주파수가 기본 주파수의 정수배라고 가정합니다. 그러나이 백서는 관심있는 사례에 (일반화 된) 알고리즘을 적용합니다.


다른 문제는 신호 모델이 올바르지 않다는 것입니다. 사용합니다 2*%pi*(1:siglen)*(Fc/siglen). 2*%pi*(0:siglen-1)*(Fc/siglen)단계가 올바르게 나오도록 사용해야 합니다.

또한 주파수 Fc=21.3가 매우 낮다 는 문제가 있다고 생각 합니다. 저주파 실수 값 신호는 위상 / 주파수 추정 문제와 관련하여 바이어스를 나타내는 경향이 있습니다.

또한 위상 추정치에 대한 대략적인 그리드 검색을 시도했으며 Goertzel 알고리즘과 동일한 대답을 제공합니다.

아래는 두 가지 다른 주파수 Fc=21.3(고체)와 Fc=210.3(대시)에 대한 두 추정치 (Goertzel : blue, Coarse : red)의 바이어스를 보여주는 그림입니다 . 보다시피 더 높은 주파수에 대한 바이어스는 훨씬 적습니다.

플롯 축은 초기 위상이 0에서 로 변경됩니다 .2 πx2π

여기에 이미지 설명을 입력하십시오


종이를 기반으로 Goerzel 알고리즘의 코드를 테스트했습니다. 출력 DTFT 값을 사용하면 피크를 매우 정확하게 얻을 수 있습니다. 그러나 스케일링 계수는 정확히 1000입니다. 따라서 원래 피크가 1,234이면 Goerzel 이후에는 1234가됩니다. 어디에서 왔는지 아는 사람이 있습니까?
lR8n6i

그 동안 조사를 해 보았습니다. 아마도 이것은 진폭 스케일링과 관련이있을 것입니다 : 스케일링 시간 도메인 진폭 = 주파수 도메인 계수 * 2 / N, 여기서 N은 신호의 길이입니다. 이 가정이 맞습니까?
lR8n6i


안녕하세요! Goertzel 알고리즘을 사용하면 복잡한 계수의 진폭이 매우 정확하지만 위상이 완전히 잘못되었음을 알았습니다. 누군가 이것이 어디서 올 수 있는지에 대한 아이디어가 있습니까? "위상"이란 원래 신호의 기본에 지정된 위상 지연을 의미합니다.
lR8n6i

1
sin(ω0t+ϕ)j2[ejϕδ~(ω+ω0+2πk)e+jϕδ~(ωω0+2πk)]π/2

4

2 개가 아닌 여러 개의 인접한 FFT 빈을 기꺼이 사용하려는 경우 복잡한 빈 결과 사이의 윈도 잉 된 Sinc 보간은 창의 너비에 따라 매우 정확한 추정치를 생성 할 수 있습니다.

Windowed Sinc 보간은 일반적으로 고품질 오디오 업 샘플러에서 발견되므로 해당 주제의 논문은 오류 분석에 적합한 보간 공식을 갖습니다.


의견 주셔서 감사합니다. 이 접근법도 시도해 볼 것입니다.
lR8n6i

4

(π엑스)(π엑스)

[1] JL Flanagan과 RM Golden,“Phase vocoder”, Bell Systems Technical Journal, vol. 45, pp. 1493–1509, 1966 년.

[2] K. Dressler, "다중 해상도 FFT의 효율적인 구현을 이용한 사인파 추출"Proc. 9 번 국제 공항 교수 디지털 오디오 효과 (DAFx-06), 캐나다 몬트리올, 2006 년 9 월, 247–252 쪽.


안녕하세요! 모든 의견에 감사드립니다. Goertzel 필터와 포물선 피크 보간을 결합하여 위상을 얻도록 코드를 확장했습니다 (아래 참조). 그러나 결과는 여전히 정확하지 않습니다 (+ -3-4deg). 이것이 이해에 가깝거나 이해하는 데 실수가 있습니까?
lR8n6i


3

몇 년 전에이 정확한 문제에 많은 어려움을 겪었습니다.

이 질문을 게시했습니다.

/programming/4633203/extracting-precise-frequencies-from-fft-bins-using-phase-change-between-frames

나는 처음부터 계산을 끝내고 내 자신의 질문에 대한 답변을 게시했습니다.

인터넷에서 비슷한 박람회를 찾을 수 없다는 것에 놀랐습니다.

여기에 답변을 다시 게시하겠습니다. 이 코드는 FFT 창을 4x 겹치는 시나리오를 위해 설계되었습니다.

π


이 퍼즐은 잠금을 해제하기 위해 두 개의 키가 필요합니다.

그래프 3.3 :

여기에 이미지 설명을 입력하십시오

그래프 3.4 :

여기에 이미지 설명을 입력하십시오

암호:

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;
}

주파수를 보간하는 반면 OP는 주파수를 알고 진폭을 보간하려고합니다.
finnw

2

이 파이썬 코드는 포물선 보간 (McAulay Quatieri, Serra 등이 고조파 + 잔차에서 성공적으로 사용한 방법)으로 매우 정확한 결과를 얻습니다 (많은 음표에 사용했으며 반음의 0.01 % 미만의 오류를 얻었습니다) . 분리 기술)

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

다루고있는 주파수 (8kHz에서 샘플링 된 21.3Hz)는 매우 낮습니다. 이들은 실제 값 신호이기 때문에 ** 모든 ** 주파수에 대한 위상 추정에서 바이어스를 나타냅니다.

이 그림은에 대한 바이어스 ( phase_est - phase_orig) Fc = 210.3;와 빨간색에 대한 바이어스의 플롯을 보여줍니다 Fc = 21.3;. 보다시피, 오프셋은이 경우에 훨씬 더 중요합니다 21.3.

다른 옵션은 샘플링 속도를 줄이는 것입니다. 녹색 곡선은 Fs = 800대신에 바이어스를 나타냅니다 8000.

여기에 이미지 설명을 입력하십시오


1
업데이트 해 주셔서 감사합니다! 내 음모를보십시오; 나는 여전히 모든 위상 추정 기가이 낮은 주파수에 대한 바이어스를 가질 것이라고 생각 합니다 . 이를 해결하는 한 가지 방법은 알려진 주파수 (알려진 경우!)를 사용하여 룩업 테이블을 통해 위상 추정 바이어스를 수정하는 것입니다. 그러나 조심해야합니다 : 바이어스는 주파수에 따라 변경됩니다. 또 다른 방법은 샘플링 속도를 줄이는 것입니다.
Peter K.

1
감사합니다! 그러나 210.3 바이어스 대신 Fs = 8000 Hz 및 Fc = 210을 사용하는 경우 훨씬 더 나빠 보입니다. 어디에서 왔는지 아십니까?
lR8n6i

1
어크! 몰라. 측지 자 추정기는 다음과 같은 문제가 없습니다 goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;. :-) 조금 더 파낼 것입니다. 이 공간을보십시오.
Peter K.

1
포물선 보간이 생각하는 것을 수행하지 않습니다. 특히의 계산을 pp2 = (abs(y(3)) - abs(y(1)))/(2*(2*abs(y(2)) - abs(y(3)) - abs(y(1)))); phase2 = y0 - 0.25*(ym1-yp1)*p2;바꾸면 더 나은 답변을 얻을 수 Fc=210있습니다. 나는 현재 버전이 p당신에게 합리적인 것을 줄 것이라고 확신하지 못한다. 보간 공식은 포물선의 AMPLITUDE의 보간을위한 것이지만, 홀수 인 위상p보간하는 것 입니다.
Peter K.

1
p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))진폭 대신 PHASES를 사용 하는 경우 피크 위치 ( )가 때때로 부정확 하다는 점을 제외하고는 모두 정상 입니다. 이는 위상 +/- 180도 경계를 뛰어 넘을 있기 때문 입니다. 단계를 위해 그것을 고치는 데 필요한 것은 p2위의 계산으로 해당 줄을 변경하는 것 입니다.
Peter K.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.