Отримайте пікове значення сигналу, якщо його частота лежить між двома центрами


12

Припустімо наступне:

  • Частота основного сигналу була оцінена за допомогою FFT та деяких методів оцінки частоти і лежить між двома центрами
  • Частота вибірки фіксована
  • Обчислювальні зусилля - це не проблема

Знаючи частоту, який найточніший спосіб оцінити відповідне пікове значення фундаментальних сигналів?

Одним із способів може бути зворотний сигнал часу на нуль для збільшення роздільної здатності FFT таким чином, що центр бін буде ближче до передбачуваної частоти. У цьому сценарії один момент, в якому я не впевнений, полягає в тому, чи можу я зробити нульовий пробіл стільки, скільки хочу, чи є деякі недоліки в цьому. Інший - це те, який центр бін я повинен вибрати після нульової прокладки як той, з якого я отримую пікове значення (тому що він може не вражати частоту, що цікавить, навіть після нульового набивання).

Однак мені також цікаво, чи існує інший метод, який може дати кращі результати, наприклад, оцінювач, який використовує пікові значення навколишніх двох центрів біна для оцінки пікового значення на частоті, що цікавить.


2
нульова накладка перед FFT - це один із способів. Інший - застосувати функцію вікна, яка підходить для ваших неделів. Плоский верхнє вікно було розроблено саме для цієї мети. Звичайно, якщо ви вже точно знаєте частоту і вас цікавить лише одна амплутида, можливо, є більш дешеві способи зробити це, ніж FFT.
sellibitze

1
не потрібні нульові прокладки: проста параболічна інтерполяція (з 3 балами: imax-1, imax, imax + 1, де imaxпік FFT) дасть точні результати
Basj

Переконайтесь, що функція інтерполяції відповідає віконній функції. Плоский верх - тривіальний, інакше потрібно пара, що відповідає (наприклад, прямокутне вікно + синпокалізація, гауссова вікно + гауссова інтерполяція тощо)
finnw

@CedronDawg це питання та його відповіді пов'язані (але не однакові) з вашою точною формулою частоти. Можливо, вам це буде цікаво.
Fat32

Відповіді:


5

Перший алгоритм, який спадає на думку, - це алгоритм Ґерцеля . Цей алгоритм зазвичай передбачає, що частота, що цікавить, є цілим числом, кратним основній частоті. Однак у цьому документі застосовується (узагальнений) алгоритм до випадку, який вас цікавить.


Ще одна проблема полягає в тому, що модель сигналу неправильна. Він використовує 2*%pi*(1:siglen)*(Fc/siglen). Він повинен використовуватись 2*%pi*(0:siglen-1)*(Fc/siglen)для того, щоб фаза вийшла правильно.

Я також думаю, що існує проблема з Fc=21.3дуже низькою частотою . Сигнали з низькою частотою в реальному значенні мають тенденцію виявляти зміщення, коли мова йде про проблеми оцінки фази / частоти.

Я також спробував пошук грубої сітки для фазової оцінки, і вона дає ту саму відповідь, що і алгоритм Ґерцеля.

Нижче наведено графік, який показує зміщення в обох оцінках (Ґерцель: синій, грубо: червоний) для двох різних частот: Fc=21.3(суцільна) та Fc=210.3(пунктирна). Як ви бачите, упередження для більш високої частоти набагато менше.

x2π

введіть тут опис зображення


Щойно випробуваний код алгоритму Ґерцеля на основі паперу. Використовуючи вихідне значення DTFT, пік можна отримати дуже точно. Однак є коефіцієнт масштабування рівно 1000. Отже, якщо початковий пік дорівнює 1234, після Герцеля це буде 1234. Хтось знає, звідки це могло взятись?
lR8n6i

Тим часом зробив якісь дослідження. Можливо, це стосується масштабування амплітуди: масштабування амплітуди доменної зони = коефіцієнт доменної частоти * 2 / N, де N - довжина сигналу. Чи правильно це припущення?
lR8n6i


Привіт! Я щойно з’ясував, що використовуючи алгоритм Ґерцеля, амплітуда за отриманим складним коефіцієнтом дуже точна, але фаза зовсім неправильна. Хтось має ідею, звідки це могло взятись? Під "фазою" я маю на увазі відставання фази, вказане в основній частині вихідного сигналу.
lR8n6i

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

4

Якщо ви готові використовувати декілька сусідніх бункерів FFT, а не лише 2, то інтерполяція Sinc між вікнами між складними результатами сміття може дати дуже точну оцінку залежно від ширини вікна.

Інтерполяція з віконним сінцем зазвичай зустрічається у високоякісних аудіопробниках звуку, тому документи з цього питання матимуть відповідні інтерполяційні формули з аналізом помилок.


Дякуємо за коментар Я також спробую цей підхід.
lR8n6i

4

sin(πx)(πx)

[1] Дж. Л. Фланаган і Р. М. Голден, "Фазовий вокодер", Технічний журнал Bell Systems, vol. 45. С. 1493–1509, 1966.

[2] К. Дрезерлер, “Синусоїдальна витяжка з використанням ефективної реалізації ПЗП з різною роздільною здатністю” в Зб. 9-а міжнар. Конф. про цифрові аудіоефекти (DAFx-06), Монреаль, Канада, вересень 2006, стор. 247–252.


Привіт! Дякую за всі ваші коментарі. Я розширив свій код (див. Нижче), щоб поєднати фільтр Ґерцеля з параболічною піковою інтерполяцією, щоб отримати фазу. Однак результати все ще не точні (+ - 3-4deg). Це так близько, як це стає або є помилки в розумінні чи кодуванні?
lR8n6i


3

Пару років тому я мав багато труднощів із цією точною проблемою.

Я опублікував це питання:

/programming/4633203/extracting-precision-frequency-from-fft-bins-using-phase-change-bet Between-frames

Я закінчив робити розрахунки з нуля і розмістив відповідь на власне запитання.

Я здивований, що мені не вдалося знайти жодної подібної експозиції в Інтернеті.

Я знову опублікую відповідь тут; зауважте, що код призначений для сценарію, в якому я перекриваю вікно FFT на 4 рази.

π


Ця головоломка має дві клавіші, щоб розблокувати її.

Графік 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;
}

Ви інтерполюєте частоту, тоді як ОП знає частоту і хоче інтерполювати амплітуду.
finnw

2

Цей пітон-код дасть вам дуже точний результат (я використовував його для безлічі музичних нот та помилок менше 0,01% півтону) з параболічною інтерполяцією (метод, який успішно застосовується McAulay Quatieri, Serra тощо) у гармонічному + залишковому методи поділу)

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

Частоти, з якими ви маєте справу (21,3 Гц у вибірці 8 кГц), дуже низькі. Оскільки це реальні значення сигналів, вони демонструватимуть зміщення фазової оцінки ** будь-якої ** частоти.

На цьому малюнку зображено графік зміщення ( phase_est - phase_orig) для Fc = 210.3;(червоного кольору) проти зміщення для Fc = 21.3;. Як бачимо, залік набагато важливіший для 21.3справи.

Інший варіант - зменшити частоту вибірки. Зелена крива показує ухил Fs = 800замість 8000.

введіть тут опис зображення


1
Дякуємо за оновлення! Дивіться мій сюжет; Я все ще думаю, що будь-який фазовий оцінювач буде мати зміщення для такої низької частоти. Один із способів обійти це - використовувати відому частоту (якщо вона відома!) Для виправлення зміщення фазової оцінки через таблицю огляду. Але вам потрібно бути обережними: зміщення змінюватиметься з частотою. Ще один спосіб зробити це - зменшити показник вибірки.
Петро К.

1
Дякую і вам! Однак якщо ви використовуєте Fs = 8000 Гц і Fc = 210 замість 210,3, зміщення виглядає ще гірше. Будь-яка ідея, звідки це могло взятись?
lR8n6i

1
Ерк! Не маю уявлення. FWIW, оцінювач Geortzel не має проблем: goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;. :-) Покопаю ще трохи. Слідкуйте за цим простором.
Петро К.

1
Параболічна інтерполяція - це не те, що ви думаєте, що робить. Зокрема, якщо ви заміните розрахунок pз , 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;то ви отримаєте набагато кращі відповіді --- навіть Fc=210. Я зовсім не впевнений, що поточна версія програми pнадасть вам щось розумне. Формула інтерполяції призначена для інтерполяції ПОШУКУ параболи, але pє інтерполяцією фази, яка просто ... дивна.
Петро К.

1
Все це гаразд, ЗАКЛЮЧИТЬ, що місце розташування піку ( p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))) буде неправильним деякий час, якщо ви використовуєте ФАЗИ замість амплітуд. Це відбувається тому, що фази можуть стрибати навколо межі +/- 180 градусів. Все, що потрібно, щоб виправити це на етапі, - це змінити цей рядок на мій p2розрахунок вище.
Петро К.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.