Frekansı iki bölme merkezi arasında bulunuyorsa, bir sinyalin tepe değerini alın


12

Lütfen aşağıdakileri varsayalım:

  • Bir sinyalin temel frekansı FFT ve bazı frekans tahmin yöntemleri kullanılarak tahmin edilmiştir ve iki kutu merkezi arasında uzanmaktadır
  • Örnekleme frekansı sabittir
  • Hesaplamalı çaba sorun değil

Frekansı bilmek, temel sinyallerin karşılık gelen tepe değerini tahmin etmenin en doğru yolu nedir?

Bir yol, hazne merkezi tahmini frekansa daha yakın olacak şekilde FFT çözünürlüğünü artırmak için zaman sinyalini sıfırlamak olabilir. Bu senaryoda, emin olmadığım bir nokta istediğim kadar sıfır-pad olup olmadığını veya bunu yaparken bazı dezavantajları olup olmadığıdır. Bir diğeri, en yüksek değeri elde ettiğim gibi sıfır doldurma işleminden sonra hangi bölme merkezini seçmem gerektiğidir (çünkü biri, sıfır doldurma işleminden sonra bile ilgili frekansa tam olarak vuramayabilir).

Bununla birlikte, daha iyi sonuçlar verebilecek başka bir yöntem olup olmadığını merak ediyorum, ilgi frekansındaki tepe değerini tahmin etmek için çevreleyen iki bin merkezinin tepe değerlerini kullanan bir tahminci.


2
FFT'den önce sıfır dolgu bir yoludur. Diğeri ise, uçlarınız için uygun bir pencere fonksiyonu uygulamaktır. Düz üst pencere tam olarak bu amaçla tasarlanmıştır. Tabii ki, frekansı zaten tam olarak biliyorsanız ve sadece bir amplutide ilgi duyuyorsanız, bunu yapmanın muhtemelen bir FFT'den daha ucuz yolları vardır.
sellibitze

1
sıfır dolgusu gerekmez: basit parabolik enterpolasyon (3 nokta ile: imax-1, imax, imax + 1, imaxFFT zirvesi nerede ) size doğru sonuçlar verecektir
Basj

Enterpolasyon fonksiyonunun pencere fonksiyonuyla eşleştiğinden emin olun. Flat-top önemsizdir, aksi takdirde eşleşen bir çift istersiniz (örn. Dikdörtgen pencere + samimi enterpolasyon, gauss penceresi + gauss enterpolasyonu vb.)
finnw

@CedronDawg Bu soru ve cevapları tam frekans formülünüzle ilişkilidir (aynı değildir). Belki ilginç bulabilirsiniz.
Fat32

Yanıtlar:


5

İlk akla gelen algoritma Goertzel Algoritmasıdır . Bu algoritma genellikle ilgili frekansın temel frekansın bir tam sayı katı olduğunu varsayar. Ancak, bu çalışma ilgilendiğiniz duruma (genelleştirilmiş) algoritmayı uygulamaktadır.


Başka bir sorun, sinyal modelinin yanlış olmasıdır. Kullanır 2*%pi*(1:siglen)*(Fc/siglen). Fazın 2*%pi*(0:siglen-1)*(Fc/siglen)doğru bir şekilde çıkması için kullanılmalıdır .

Ayrıca sıklığın Fc=21.3çok düşük olması ile ilgili bir sorun olduğunu düşünüyorum . Düşük frekanslı gerçek değerli sinyaller, faz / frekans tahmin sorunları söz konusu olduğunda yanlılık gösterme eğilimindedir.

Ayrıca faz tahmini için kaba bir ızgara araması denedim ve Goertzel algoritmasıyla aynı cevabı veriyor.

Aşağıda, her iki tahmindeki (Goertzel: mavi, Kaba: kırmızı) iki farklı frekans için yanlılığı gösteren bir grafik bulunmaktadır: Fc=21.3(katı) ve Fc=210.3(kesikli). Gördüğünüz gibi, yüksek frekansın sapması çok daha azdır.

-axis grafiği 0'dan 2 π'ya değişen ilk fazdır .x2π

resim açıklamasını buraya girin


Kağıda göre Goerzel algoritması için kodu test ettim. Çıkış DTFT değeri kullanılarak, pik çok doğru bir şekilde elde edilebilir. Bununla birlikte, tam olarak 1000'lik bir ölçeklendirme faktörü vardır. Yani, orijinal zirve 1.234 ise, Goerzel'den sonra 1234 olacaktır. Bunun nereden gelebileceğini bilen var mı?
lR8n6i

Bu arada biraz araştırma yaptım. Muhtemelen genlik ölçeklendirmesi ile ilgilidir: ölçekleme süresi etki alanı genliği = frekans etki alanı katsayısı * 2 / N, burada N, sinyal uzunluğudur. Bu varsayım doğru mu?
lR8n6i


Selam! Az önce Goertzel algoritmasını kullanarak, ortaya çıkan karmaşık katsayıdaki genliğin çok doğru olduğunu, ancak fazın tamamen yanlış olduğunu öğrendim. Birinin bunun nereden gelebileceği hakkında bir fikri var mı? "Faz" ile, orijinal sinyalin temelinde belirtilen faz gecikmesini kastediyorum.
lR8n6i

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

4

Sadece 2 değil, birden fazla komşu FFT bölmesi kullanmak istiyorsanız, pencereli Karmaşık bölme sonuçları arasındaki Sinc enterpolasyonu, pencerenin genişliğine bağlı olarak çok doğru bir tahmin üretebilir.

Pencereli Sinc enterpolasyonu genellikle yüksek kaliteli ses örnekleyicilerinde bulunur, bu nedenle bu konudaki kağıtlarda hata analizi ile uygun enterpolasyon formülleri olacaktır.


Yorum için teşekkürler. Ben de bu yaklaşımı deneyeceğim.
lR8n6i

4

sin(πx)(πx)

[1] JL Flanagan ve RM Golden, “Faz vocoder” Bell Systems Technical Journal, cilt. 45, sayfa 1493-1509, 1966.

[2] K. Dressler, “Çok çözünürlüklü FFT'nin verimli bir şekilde uygulanmasıyla sinüsoidal ekstraksiyon”, Proc. 9th Int. Konf. Dijital Ses Efektleri (DAFx-06) hakkında, Montreal, Kanada, Eylül 2006, s. 247-252.


Selam! Tüm yorumlarınız için çok teşekkürler. Aşama elde etmek için Goertzel filtresini parabolik tepe enterpolasyonu ile birleştirmek için kodumu genişlettim (aşağıya bakın). Ancak, sonuçlar hala doğru değildir (+ - 3-4deg). Bu olabildiğince yakın mı veya anlama veya kodlamada hatalar var mı?
lR8n6i


3

Birkaç yıl önce bu sorunla ilgili çok zorlandım.

Bu soruyu gönderdim:

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

Hesaplamaları sıfırdan yaptım ve kendi soruma bir cevap gönderdim.

İnternette benzer bir fuar bulamadığım için şaşırdım.

Cevabı burada tekrar göndereceğim; Kodun, FFT penceremi 4x ile çakıştığım bir senaryo için tasarlandığını unutmayın.

π


Bu bulmaca kilidini açmak için iki anahtar alır.

Grafik 3.3:

resim açıklamasını buraya girin

Grafik 3.4:

resim açıklamasını buraya girin

Kod:

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

Siz frekansı enterpolasyonluyorsunuz, OP frekansı biliyor ve genliği enterpole etmek istiyor.
finnw

2

Bu python kodu, parabolik enterpolasyonla (McAulay Quatieri, Serra, vb. ayırma teknikleri)

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

Karşılaştığınız frekanslar (8kHz'de örneklenmiş 21.3Hz) çok düşük. Bunlar gerçek değerli sinyaller olduğu için, ** herhangi bir ** frekans için faz tahmininde bir sapma göstereceklerdir.

Bu resim, önyargıya karşı (kırmızı renkte) yanlılığın ( phase_est - phase_orig) bir grafiğini göstermektedir . Gördüğünüz gibi, ofset dava için çok daha önemlidir .Fc = 210.3;Fc = 21.3;21.3

Başka bir seçenek de örnekleme oranınızı düşürmektir. Yeşil eğri Fs = 800bunun yerine eğimi gösterir 8000.

resim açıklamasını buraya girin


1
Güncelleme için teşekkürler! Grafiğe bakın; Hala herhangi bir faz tahmin edicisinin bu düşük frekans için bir önyargıya sahip olacağını düşünüyorum . Bunu aşmanın bir yolu, bir arama tablosundan faz tahmini sapmasını düzeltmek için bilinen frekansı (biliniyorsa!) Kullanmaktır. Ancak dikkatli olmanız gerekir: önyargı frekansla değişecektir. Bunu yapmanın başka bir yolu da örnekleme oranınızı düşürmektir.
Peter K.

1
Sana da teşekkürler! Ancak, Fs = 8000 Hz ve 210.3 yerine Fc = 210 kullanıyorsanız, daha kötü görünüyor. Bunun nereden gelebileceği hakkında bir fikrin var mı?
lR8n6i

1
Erk! Fikrim yok. FWIW, Geortzel tahmincisi problem yok: goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;. :-) Biraz daha kazacak. Bu alanı izle.
Peter K.

1
Parabolik enterpolasyon, yaptığını sandığınız şeyi yapmaz. Eğer senin hesaplama değiştirirseniz Özellikle, pile 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;o zaman bile için --- çok daha iyi cevap almak Fc=210. Şu anki sürümünün psize mantıklı bir şey vereceğinden emin değilim . İnterpolasyon formülü, bir parabolün AMPLITUDE'sinin penterpolasyonu içindir , ancak sadece ... garip olan fazı enterpolasyonludur .
Peter K.

1
Tüm bunlar sorun p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))değil, genlik yerine PHASES kullanıyorsanız , tepe konumunun ( ) bir süre yanlış olacağı durumlar dışında. Fazlar Bunun nedeni olabilir +/- 180 derece sınırını zıplayın. Faz için düzeltmek için gereken tek şey bu satırı p2yukarıdaki hesaplamamla değiştirmek .
Peter K.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.