是否有一种算法可以计算单个频率的相位?


17

如果您有一个函数,并且参考正弦波,那么计算的快速算法是什么?f(t)=Asin(ωt+ϕ)sin(ωx)ϕ

我当时在看Goertzel算法,但它似乎没有处理相位问题?

Answers:


5

在特定频率下使用DFT。然后从实部/成像部分计算振幅和相位。它为您提供了参考到采样时间开始的阶段。

在“普通” FFT(或为所有N个谐波计算的DFT)中,通常使用f = k *(sample_rate)/ N来计算频率,其中k是整数。尽管它看起来似乎是牺牲品(特别是对于全整数整数的成员),但是在执行单个DFT时,您实际上可以使用k的非整数值。

例如,假设您已生成(或获得)27 Hz的正弦波的N = 256个点。(假设sample_rate = 200)。256点FFT(或N点DFT)的“正常”频率将对应于:f = k *(sample_rate)/ N = k *(200)/ 256,其中k是整数。但是使用上面列出的参数,非整数“ k”为34.56将对应于27 Hz的频率。这就像创建一个DFT“箱”,该箱正好位于感兴趣的频率(27 Hz。)中心。一些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:我希望以上内容可以很好地转换为stackoverflow-其中一些可能会绕来绕去)

上面的结果是-twopi / 4的相位,如生成的实点所示(并且amp被加倍以反映pos / neg频率)。

需要注意的几件事–我使用余弦来生成测试波形并解释结果–您必须注意–相位参考时间= 0,这是您开始采样的时间(即:当您收集r [0]时) ),并且余弦是正确的解释)。

上面的代码既不优雅也不高效(例如:使用查找表获取sin / cos值等)。

当您使用更大的N时,您的结果将变得更加准确,并且由于采样率和上方的N不是彼此的倍数,因此会出现一点误差。

当然,如果要更改采样率N或f,则必须更改代码和k的值。您可以在连续频率线上的任何位置放下DFT仓-只需确保您使用的k值对应于感兴趣的频率即可。


可以通过调整N使k更接近整体来改进此方法。我发布了一个单独的答案,这损害了该算法的准确性。
mojuba

10

该问题可以表述为(非线性)最小二乘问题:

F(ϕ)=12i=1n[Asin(ωi+ϕ)fi(ω)]2

其中是相对于最小化的目标函数。ϕF(ϕ)ϕ

派生非常简单:

F(ϕ)=i=1nAcos(ωi+ϕ)[Asin(ωi+ϕ)fi(ω)]

可以使用梯度下降法(一阶逼近),牛顿法高斯-牛顿法莱文伯格-马夸特方法(需要提供二阶逼近来迭代地最小化上述目标函数。F(ϕ)

显然,上述目标函数由于周期性而具有多个极小值,因此可以添加一些惩罚项来区分其他极小值(例如,将到模型方程式中)。但我认为,优化只会收敛到最近的最小值和可更新的结果中减去。ϕ22πk,kN


我认为您不需要因为周期性而受到惩罚吗?您可以在收敛的相空间中取最小值,然后做模,不是吗?2π
Spacey

@Mohammad是的,但是某些优化技术可能会使用多个起点,这些起点应该收敛到相同的值,或者使用单个全局最小化器假定为凸函数,而使用二次函数可以很好地近似它。另一个好处是,对于任何起点,我们都以相同的结果结束。ϕ0
Libor 2012年

有趣。我可以邀请您也解决这个相关问题吗?:-)
Spacey 2012年

@Mohammad OK,我在那里贡献了一点:)
Libor

函数fi(w)到哪里去了?fi(w)不是常数,因此当您采用非常数的导数时,它如何变为零?
SamFisher83 2012年

5

Goertzel算法有几种不同的公式。提供2个状态变量(正交或接近)或复杂的状态变量(可能的输出)的变量通常可以用来参考Goertzel窗口中的某个点(例如中间点)来计算或估计相位。仅提供单个标量输出的输出通常不能。

您还需要知道Goertzel窗口相对于时间轴的位置。

如果您的信号在Goertzel窗口中不是完全整数周期,则窗口中间参考点周围的相位估计可能比将相位参考起点或终点更为准确。

如果您知道信号的频率,则完整的FFT会显得过大。另外,可以将Goertzel调谐到FFT长度上非周期性的频率,而FFT对于非周期性的窗口频率将需要附加的插值或零填充。

复数的Goe​​rtzel等效于DFT的1 bin,该DFT对余弦和正弦基向量或FFT旋转因子使用递归。


相位估计是否在窗口内的任何地方都没有完全相同的精度,因为您只需在窗口开始处的相位估计上加上即可计算窗口内样本的相位估计(是窗口的开始)?ωkkk=0
Olli Niemitalo

不可以,因为添加wk会导致窗口结束时的相位不同于非整数周期光圈正弦曲线的起始相位。但是1 bin DFT在同一点计算单个循环相位。因此,这三个值将全部不同。但是中心相位总是与奇/偶函数的比率有关,无论什么f0。
hotpaw2

尝试,但我不明白。
Olli Niemitalo

使用余弦(在k = 0时为零相位),略微调​​整频率(通过微小的无理数,但不更改在k = 0时的相位)。DFT报告阶段已更改!尝试以正好位于k = N / 2的余弦为基准。对于任何df,在k = N / 2时都没有变化。罪恶或任何混合都一样。将相位参考点居中显示,随着f0的变化,测得的相位变化较小。例如,频率误差不会增加相位测量误差。
hotpaw2

1
是的,如果正弦波和Goertzel滤波器的频率不同,则相位估计误差在窗口中心处较小。在那种情况下,在窗口末端的相位估计值被一个常数所偏置,该常数是窗口的中心与末端之间的距离与正弦波和Goertzel滤波器频率之差的乘积。减去该偏差会得到与中心估算值相同的尺寸误差,但它需要知道正弦波的频率。
Olli Niemitalo

4

如果您的信号无噪声,则可以在两个信号中都识别出零交叉,并确定频率和相对相位。


3

这取决于您对“快速”的定义,想要的估计精度,想要的是还是相对于采样的相位以及函数和参考正弦波上有多少噪声。ϕ

一种实现方法是仅获取的FFT,然后查看最接近的bin 。f(t)ω 但是,这将取决于接近bin中心频率。ω

所以:

  • “快速”是什么意思?
  • 您需要多少精度的估算?
  • 您是否想要(相对于参考的相位)或相对于采样开始的相位?有关系吗?ϕ
  • 每个信号的噪声水平是多少?

PS:我假设您的意思是,而不是。f(t)=Asin(ωt+ϕ)f(t)=Asin(ωx+ϕ)


2

起点:
1)将信号和参考正弦波相乘: =A⋅sin(ωt+ ϕ)⋅sin(ωt)=0.5⋅A⋅(cos(ϕ)-cos(2⋅ωt+ ϕ) ) 2)在周期上找到积分: 3)您可以计算:
F(t)
T=π/ω
I(ϕ)=0TF(t)dt =0.5Acos(ϕ)T
ϕ
cos(ϕ)=I(t)/(0.5AT)

想一想:
如何测量A?
如何确定在间隔?(想想“参考cos波”)ϕ0..(2π)

对于离散信号,请更改积分之和,并仔细选择T!


1

您也可以这样做(以numpy表示法):

np.arctan( (signal*cos).sum() / (signal*sin).sum() ))

其中signal是您的相移信号,cos和sin是参考信号,您可以通过将两个乘积相加来在一定时间内生成积分的近似值。


0

这是对@Kevin McGee的建议的改进,该建议使用具有分数bin索引的单频DFT。凯文(Kevin)的算法效果不佳:虽然在半垃圾箱和整个垃圾箱中非常精确,但也接近整体和一半,也相当不错,但是否则误差可能在5%之内,这对于大多数任务来说可能是不可接受的。

我建议通过调整来改进Kevin算法,即DFT窗口的长度,以使尽可能接近整体。这是可行的,因为与FFT不同,DFT不需要为2的幂。NkN

下面的代码在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))

FFT只是有效地计算DFT的一种方法。对于现代图书馆,两个限制的力量不再存在。如果只需要一个或两个bin值,则最好像您一样直接计算它们。对于单个纯音(实数或复数),仅需要两个bin值即可精确计算频率,相位和幅度。参见dsprelated.com/showarticle/1284.php。数学是相当复杂的,但是有一些链接可以解释派生词。线性代数是真正理解的前提。
Cedron Dawg,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.