Existe um algoritmo para calcular a fase para uma única frequência?

17

Se você tem uma função f(t)=Asin(ωt+ϕ) e faz referência à onda sin(ωx) qual seria um algoritmo rápido para calcular ϕ ?

Eu estava olhando para o algoritmo de Goertzel , mas ele não parece lidar com a fase?

SamFisher83
fonte

Respostas:

5

Use um DFT na frequência específica. Em seguida, calcule a amplitude e a fase das partes real / imag. Ele fornece a fase referida ao início do tempo de amostragem.

Em uma FFT 'normal' (ou uma DFT calculada para todos os N harmônicos), você normalmente calcula a frequência com f = k * (sample_rate) / N, onde k é um número inteiro. Embora possa parecer sacrílego (especialmente para membros da Igreja do Inteiro Inteiro), você pode realmente usar valores não inteiros de k ao fazer uma única DFT.

Por exemplo, suponha que você tenha gerado (ou obtido) N = 256 pontos de uma onda senoidal de 27 Hz. (digamos, sample_rate = 200). Suas frequências 'normais' para uma FFT de 256 pontos (ou D N de ponto N) corresponderiam a: f = k * (sample_rate) / N = k * (200) / 256, em que k é um número inteiro. Mas um 'k' não inteiro de 34,56 corresponderia a uma frequência de 27 Hz., Usando os parâmetros listados acima. É como criar um 'compartimento' de DFT que esteja exatamente centralizado na frequência de interesse (27 Hz.). Alguns códigos C ++ (compilador DevC ++) podem ter a seguinte aparência:

#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: Espero que o texto acima traduza bem o stackoverflow - alguns deles podem ser contornados)

O resultado do exposto acima é uma fase de -twopi / 4, conforme mostrado nos pontos reais gerados (e o amplificador é duplicado para refletir a frequência pos / neg).

Algumas coisas a serem observadas - eu uso o cosseno para gerar a forma de onda de teste e interpretar os resultados - você precisa ter cuidado com isso - a fase é referenciada no tempo = 0, que é quando você começou a amostrar (ou seja, quando você coletou r [0] ) e cosseno é a interpretação correta).

O código acima não é elegante nem eficiente (por exemplo: use tabelas de consulta para os valores sin / cos, etc.).

Seus resultados ficarão mais precisos quando você usar N maior, e há um pouco de erro devido ao fato de a taxa de amostragem e N acima não serem múltiplos um do outro.

Obviamente, se você quiser alterar sua taxa de amostragem, N ou f, precisará alterar o código e o valor de k. Você pode colocar uma bandeja DFT em qualquer lugar da linha de frequência contínua - apenas certifique-se de usar um valor de k que corresponde à frequência de interesse.

Kevin McGee
fonte
Essa abordagem pode ser aprimorada ajustando N para tornar k mais próximo de um todo. Publiquei uma resposta separada que melhora a precisão desse algoritmo.
Mjuba
10

O problema pode ser formulado como problema dos mínimos quadrados (não linear):

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

F(ϕ)ϕ

A derivada é muito simples:

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

F(ϕ)

Obviamente, a função objetivo acima tem vários mínimos por causa da periodicidade, portanto, alguns termos de penalidade podem ser adicionados para discriminar outros mínimos (por exemplo, adicionando à equação do modelo). Mas acho que a otimização só vai convergir para os mínimos mais próximo e você pode atualizar o resultado subtraindo . 2 π k , k Nϕ22πk,kN

Libor
fonte
Eu não acho que você precisa penalizar por causa da periodicidade não? Você pode pegar qualquer mínimo no espaço de fase para o qual converge e executar um modulu , não? 2π
Spacey
@Mohammad Sim, mas algumas técnicas de otimização podem usar vários pontos de partida que devem convergir para o mesmo valor ou assumir uma função convexa com um minimizador global único, que pode ser bem aproximado com um quadrático. O outro benefício é que terminamos com o mesmo resultado para qualquer ponto de partida . ϕ0
Libor
Interessante. Posso convidá-lo a também fazer uma rachadura nesta questão relacionada ? :-)
Spacey
@Mohammad OK, eu ter contribuído um pouco ali :)
Libor
Para onde vai a função fi (w)? fi (w) não é uma constante; portanto, quando você tira uma derivada de uma não constante, como ela se torna zero?
SamFisher83
5

Existem várias formulações diferentes do algoritmo de Goertzel. As que fornecem 2 variáveis ​​de estado (ortogonais ou próximas a) ou uma variável de estado complexa, como saídas possíveis, muitas vezes podem ser usadas para calcular ou estimar a fase com referência a algum ponto da janela de Goertzel, como o meio. Aqueles que fornecem apenas uma saída escalar normalmente não podem.

Você também precisará saber onde está sua janela do Goertzel em relação ao seu eixo temporal.

Se o seu sinal não for exatamente inteiro periódico na janela do Goertzel, a estimativa da fase em torno de um ponto de referência no meio da janela poderá ser mais precisa do que a fase de referência no início ou no final.

Uma FFT completa é um exagero se você souber a frequência do seu sinal. Além disso, um Goertzel pode ser sintonizado para uma frequência não periódica no comprimento da FFT, enquanto uma FFT precisará de interpolação adicional ou preenchimento zero para frequências não periódicas na janela.

Um Goertzel complexo é equivalente a 1 bin de uma DFT que usa uma recorrência para os vetores de base cosseno e senoidal ou fatores de variação da FFT.

hotpaw2
fonte
A estimativa de fase não está em nenhum lugar da janela exatamente da mesma precisão, porque você adicionaria à estimativa de fase no início da janela para calcular a estimativa de fase na amostra dentro da janela ( sendo o começo da janela)? k k = 0ωkkk=0
Olli Niemitalo
Não, porque adicionar wk resulta em uma fase diferente no final da janela e no início de um sinusóide não inteiro-periódico na abertura. Mas um DFT de 1 bin calcula uma única fase circular no mesmo ponto. Assim, os 3 valores serão todos diferentes. Mas a fase central está sempre relacionada à razão da função ímpar / par, não importa qual f0.
hotpaw2
Tentando, mas eu não entendo isso.
Olli Niemitalo 8/01/19
Use um cosseno (fase de zero em k = 0), ajuste a frequência levemente (por um pequeno número irracional, mas sem alterar a fase em k = 0). Um DFT relata que a fase mudou! Tente o mesmo com um cosseno exatamente centrado em k = N / 2. Nenhuma alteração em k = N / 2 para qualquer df. O mesmo para o pecado ou qualquer mistura. A centralização do ponto de referência da fase mostra menos alterações na fase medida com alterações em f0. por exemplo, erro de frequência não contribui para o aumento dos erros de medição de fase.
hotpaw2
1
Sim, o erro de estimativa de fase sendo menor no centro da janela faz sentido se o sinusóide e o filtro Goertzel estiverem em frequências diferentes. Nesse caso, a estimativa de fase, no final da janela, é influenciada por uma constante que é o produto da distância entre o centro e o final da janela e a diferença entre as frequências do filtro senoidal e Goertzel. Subtrair esse viés gera o mesmo erro de tamanho da estimativa central, mas requer o conhecimento da frequência do sinusóide.
Olli Niemitalo 9/01/19
4

Se seus sinais estiverem livres de ruído, você poderá identificar cruzamentos zero em ambos e determinar a frequência e a fase relativa.

Juancho
fonte
3

Isso depende de qual é a sua definição de "rápido", quão precisa você deseja sua estimativa, se deseja ou a fase relativa às suas amostragens e quanto ruído há em sua função e onda senoidal de referência.ϕ

Uma maneira de fazer isso é pegar a FFT de e apenas olhar para a lixeira mais próxima de . ωf(t)ω No entanto, isso dependerá de estar próximo da frequência do centro da bandeja.ω

Assim:

  • O que você quer dizer com "rápido"?
  • Qual é a precisão da estimativa?
  • ϕ
  • Qual é o nível de ruído em cada sinal?

PS: Eu estou assumindo que você quis dizer , em vez de .f(t)=Asin(ωt+ϕ)f(t)=Asin(ωx+ϕ)

Peter K.
fonte
2

Ponto de partida:
1) multiplique seu sinal e a onda de pecado de referência: = A⋅sin (ωt + ϕ) ⋅sin (ωt) = 0.5⋅A⋅ (cos (ϕ) - cos (2⋅ωt + ϕ) ) 2) encontre integral no período : 3) você pode calcular :
T = π / ω I ( ϕ ) = T 0 F ( t ) d t = 0,5 A c o s ( ϕ ) T ϕ c o s ( ϕ ) = I ( t ) / ( 0,5 A T )F(t)
T=π/ω
I(ϕ)=0TF(t)dt =0.5Acos(ϕ)T
ϕ
cos(ϕ)=I(t)/(0.5AT)

Pense em:
como medir A?
como determinar no intervalo ? (pense em " onda cos de referência ")0 .. ( 2 ¸ )ϕ0..(2π)

Para sinal discreto, altere a integral para somar e escolha cuidadosamente T!

SergV
fonte
1

Você também pode fazer isso (em notação numpy):

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

onde o sinal é o seu sinal de mudança de fase, cos e sin são os sinais de referência, e você gera uma aproximação de uma integral ao longo de um certo tempo através da soma dos dois produtos.

M529
fonte
0

Essa é uma melhoria na sugestão de @Kevin McGee de usar um DFT de frequência única com um índice de escaneamento fracionário. O algoritmo de Kevin não produz ótimos resultados: embora com meias caixas e caixas inteiras seja muito preciso, também próximo aos totais e metades também é muito bom, mas, caso contrário, o erro pode estar dentro de 5%, o que provavelmente não é aceitável para a maioria das tarefas .

Sugiro melhorar o algoritmo de Kevin ajustando , ou seja, o comprimento da janela DFT para que fique o mais próximo possível de um todo. Isso funciona, pois, diferentemente da FFT, a DFT não exige que seja uma potência de 2.NkN

O código abaixo está no Swift, mas deve ser intuitivamente claro:

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))
mojuba
fonte
A FFT é simplesmente uma maneira de calcular uma DFT com eficiência. Nas bibliotecas modernas, o poder de duas restrições não existe mais. Se você precisar apenas de um ou dois valores de posição, é melhor calculá-los diretamente, como você fez. Para um único tom puro (real ou complexo), são necessários apenas dois valores de posição para calcular exatamente a frequência, fase e amplitude. Consulte dsprelated.com/showarticle/1284.php . A matemática é bastante sofisticada, mas há links para os artigos nos quais as derivações são explicadas. Álgebra linear é um pré-requisito para uma verdadeira compreensão.
precisa