답변:
특정 주파수에서 DFT를 사용하십시오. 그런 다음 실수 / 이마 그 부품에서 진폭과 위상을 계산합니다. 샘플링 시간의 시작과 관련된 위상을 제공합니다.
'정상'FFT (또는 모든 N 고조파에 대해 계산 된 DFT)에서 일반적으로 f = k * (sample_rate) / N으로 주파수를 계산합니다. 여기서 k는 정수입니다. 희생적인 것처럼 보일 수 있지만 (특히 홀리 정수 교회 회원들에게) 단일 DFT를 수행 할 때 정수가 아닌 k 값을 실제로 사용할 수 있습니다.
예를 들어, 27Hz의 사인파의 N = 256 점을 생성 (또는 획득)했다고 가정합니다. (sample_rate = 200이라고합시다). 256 포인트 FFT (또는 N 포인트 DFT)에 대한 '정상적인'주파수는 f = k * (sample_rate) / N = k * (200) / 256에 해당합니다. 여기서 k는 정수입니다. 그러나 34.56의 정수가 아닌 'k'는 위에 나열된 매개 변수를 사용하여 27Hz의 주파수에 해당합니다. 관심 주파수 (27Hz)의 중심에 정확하게있는 DFT 'bin'을 만드는 것과 같습니다. 일부 C ++ 코드 (DevC ++ 컴파일러)는 다음과 같습니다.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
// arguments in main needed for Dev-C++ I/O
int main (int nNumberofArgs, char* pszArgs[ ] ) {
const long N = 256 ;
double sample_rate = 200., amp, phase, t, C, S, twopi = 6.2831853071795865;
double r[N] = {0.}, i[N] = {0.}, R = 0., I = 0. ;
long n ;
// k need not be integer
double k = 34.56;
// generate real points
for (n = 0; n < N; n++) {
t = n/sample_rate;
r[n] = 10.*cos(twopi*27.*t - twopi/4.);
} // end for
// compute one DFT
for (n = 0; n < N; n++) {
C = cos(twopi*n*k/N); S = sin(twopi*n*k/N);
R = R + r[n]*C + i[n]*S;
I = I + i[n]*C - r[n]*S;
} // end for
cout<<"\n\ndft results for N = " << N << "\n";
cout<<"\nindex k real imaginary amplitude phase\n";
amp = 2*sqrt( (R/N)*(R/N) + (I/N)*(I/N) ) ;
phase = atan2( I, R ) ;
// printed R and I are scaled
printf("%4.2f\t%11.8f\t%11.8f\t%11.8f\t%11.8f\n",k,R/N,I/N,amp,phase);
cout << "\n\n";
system ("PAUSE");
return 0;
} // end main
//**** end program
(PS : 위의 내용이 스택 오버 플로우로 잘 변환되기를 바랍니다. 일부는 래핑 될 수 있습니다)
위의 결과는 생성 된 실제 포인트와 같이 -twopi / 4의 위상입니다 (그리고 amp는 pos / neg 주파수를 반영하기 위해 두 배가됩니다).
몇 가지 알아 두어야 할 사항 – 테스트 파형을 생성하고 결과를 해석하기 위해 코사인을 사용합니다.주의해야 할 점 – 위상은 샘플링을 시작할 때인 시간 = 0을 기준으로합니다 (예 : r [0]을 수집 한 시점) ), 코사인은 올바른 해석입니다).
위 코드는 우아하지도 효율적이지 않습니다 (예 : sin / cos 값 등을 위해 룩업 테이블 사용).
더 큰 N을 사용할수록 결과가 더 정확 해지며, 샘플 속도와 N이 서로의 배수가 아니기 때문에 약간의 오류가 있습니다.
물론 샘플링 속도 N 또는 f를 변경하려면 코드와 k 값을 변경해야합니다. 연속 주파수 라인의 어느 곳에서나 DFT 출력 함을 꺼낼 수 있습니다. 관심 주파수에 해당하는 k 값을 사용하고 있는지 확인하십시오.
문제는 (비선형) 최소 제곱 문제로 공식화 될 수 있습니다.
여기서 에 대해 최소화하는 목적 함수이다 .
파생 상품은 매우 간단합니다.
그라디언트 하강 법 (1 차 근사), 뉴턴 법 , 가우스-뉴턴 법 또는 Levenberg-Marquardt 법을 사용하여 위의 목적 함수를 반복적으로 최소화 할 수 있습니다 (2 차 근사 는 여기에 제공되어야 함).
분명히, 위의 목적 함수는 주기성으로 인해 여러 개의 최소값을 가지므로 다른 최소값을 식별하기 위해 일부 페널티 항을 추가 할 수 있습니다 (예 : 모델 방정식에 추가 ). 그러나 최적화가 가장 가까운 최소 점으로 수렴하고 빼는 결과를 업데이트 할 수 있다고 생각합니다 . 2 π k , k ∈ N
Goertzel 알고리즘에는 몇 가지 다른 공식이 있습니다. 2 개의 상태 변수 (직교 또는 가까운) 또는 복잡한 상태 변수를 가능한 출력으로 제공하는 것은 종종 중간과 같은 Goertzel 창의 특정 지점을 참조하여 위상을 계산하거나 추정하는 데 사용될 수 있습니다. 단일 스칼라 출력 만 제공하는 것은 보통 불가능합니다.
또한 Goertzel 창이 시간 축과 관련하여 어디에 있는지 알아야합니다.
신호가 Goertzel 윈도우에서 정확히 정수주기가 아닌 경우, 윈도우 중간의 기준점 주변의 위상 추정치가 위상을 시작 또는 끝을 참조하는 것보다 정확할 수 있습니다.
신호의 주파수를 알고 있으면 전체 FFT가 과도합니다. 또한 Goertzel은 FFT 길이에서 주기적이 아닌 주파수로 조정할 수 있지만, FFT는 비주기적인 주파수에 대해서는 추가 보간 또는 제로 패딩이 필요합니다.
복잡한 Goertzel은 코사인 및 사인 기반 벡터 또는 FFT 트위들 인자에 대한 반복을 사용하는 DFT의 1 bin에 해당합니다.
이는 "빠른"의 정의가 무엇인지, 를 원하는지 또는 샘플링과 관련된 위상 을 원하는지 , 함수 및 기준 사인파에 얼마나 많은 노이즈가 있는지에 대한 추정치의 정확성에 달려 있습니다 .
이를 수행하는 한 가지 방법은 의 FFT를 취하고 가장 가까운 bin을 보는 것 입니다. ω 그러나 이것은 가 빈 중심 주파수에 가깝다 는 것에 달려 있습니다.
그래서:
추신 : 나는 당신 이 대신 을 의미한다고 가정합니다 .
이것은 @Kevin McGee의 분수 빈 지수와 함께 단일 주파수 DFT를 사용하도록 개선 된 것입니다. Kevin의 알고리즘은 큰 결과를 얻지 못합니다. 반 빈과 전체 빈은 매우 정확하지만 전체와 가까우며 반은 꽤 좋지만 그렇지 않으면 오류가 5 % 이내에있을 수 있습니다. .
가 가능한 한 전체에 가까워 지도록 , 즉 DFT 창의 길이를 조정하여 Kevin의 알고리즘을 개선하는 것이 좋습니다 . FFT와 달리 DFT는 을 2의 거듭 제곱으로 요구하지 않기 때문에 작동합니다 .
아래 코드는 Swift에 있지만 직관적으로 명확해야합니다.
let f = 27.0 // frequency of the sinusoid we are going to generate
let S = 200.0 // sampling rate
let Nmax = 512 // max DFT window length
let twopi = 2 * Double.pi
// First, calculate k for Nmax, and then round it
var k = round(f * Double(Nmax) / S)
// The magic part: recalculate N to make k as close to whole as possible
// We also need to recalculate k once again due to rounding of N. This is important.
let N = Int(k * S / f)
k = f * Double(N) / S
// Generate the sinusoid
var r: [Double] = []
for i in 0..<N {
let t = Double(i) / S
r.append(sin(twopi * f * t))
}
// Compute single-frequency DFT
var R = 0.0, I = 0.0
let twopikn = twopi * k / Double(N)
for i in 0..<N {
let x = Double(i) * twopikn
R += r[i] * cos(x)
I += r[i] * sin(x)
}
R /= Double(N)
I /= Double(N)
let amp = 2 * sqrt(R * R + I * I)
let phase = atan2(I, R) / twopi
print(String(format: "k = %.2f R = %.8f I = %.8f A = %.8f φ/2π = %.8f", k, R, I, amp, phase))