รับค่าสูงสุดของสัญญาณหากความถี่อยู่ระหว่างสอง bin center


12

โปรดสมมติว่าต่อไปนี้:

  • ความถี่พื้นฐานของสัญญาณได้รับการประมาณโดยใช้ FFT และวิธีการประมาณความถี่บางอย่างและอยู่ระหว่างศูนย์ bin สองแห่ง
  • ความถี่ในการสุ่มตัวอย่างได้รับการแก้ไข
  • ความพยายามในการคำนวณไม่ใช่ปัญหา

เมื่อทราบความถี่แล้ววิธีใดที่แม่นยำที่สุดในการประเมินค่าสูงสุดของสัญญาณขั้นพื้นฐาน

วิธีหนึ่งอาจเป็นสัญญาณแผ่นเวลาเป็นศูนย์เพื่อเพิ่มความละเอียด FFT เช่นที่ศูนย์ช่องเก็บจะอยู่ใกล้กับความถี่โดยประมาณ ในสถานการณ์นี้จุดหนึ่งที่ฉันไม่แน่ใจเกี่ยวกับคือถ้าฉันสามารถเป็นศูนย์ได้มากเท่าที่ฉันต้องการหรือหากมีข้อบกพร่องบางอย่างในการทำเช่นนั้น อีกอันหนึ่งคือศูนย์ bin ที่ฉันควรเลือกหลังจากการเติมศูนย์เป็นศูนย์ที่ฉันได้รับค่าสูงสุดจาก (เพราะอาจไม่มีความถี่ของดอกเบี้ยที่น่าสนใจแม้แต่หลังจากการเติมเต็มศูนย์)

อย่างไรก็ตามฉันยังสงสัยว่ามีวิธีอื่นที่อาจให้ผลลัพธ์ที่ดีกว่าได้หรือไม่ผู้ประเมินที่ใช้ค่าสูงสุดของศูนย์ bin สองแห่งรอบข้างเพื่อประเมินมูลค่าสูงสุดตามความถี่ที่น่าสนใจ


2
การเติมเต็มศูนย์ก่อน FFT เป็นวิธีหนึ่ง อีกอันหนึ่งคือการใช้ฟังก์ชั่นหน้าต่างที่เหมาะกับ neads ของคุณ หน้าต่างด้านบนแบนถูกออกแบบมาเพื่อจุดประสงค์นี้อย่างแท้จริง แน่นอนถ้าคุณรู้ความถี่แล้วและคุณสนใจแอมพลิจูดเพียงอันเดียวคงมีวิธีที่ถูกกว่า FFT
sellibitze

1
ไม่ต้องใช้ช่องว่างภายใน: การแก้ไขพาราโบลาอย่างง่าย (ด้วย 3 คะแนน: imax-1, imax, imax + 1, imaxจุดสูงสุด FFT) จะให้ผลลัพธ์ที่แม่นยำ
Basj

ตรวจสอบให้แน่ใจว่าฟังก์ชั่นการแก้ไขตรงกับฟังก์ชั่นหน้าต่าง Flat-top เป็นเรื่องไม่สำคัญไม่เช่นนั้นคุณต้องการคู่ที่ตรงกัน (เช่นหน้าต่างสี่เหลี่ยม + การแก้ไข Sinc, หน้าต่าง Gaussian + การแก้ไข Gaussian เป็นต้น)
finnw

@CedronDawg คำถามนี้และคำตอบนั้นเกี่ยวข้องกัน (แต่ไม่เหมือนกัน) กับสูตรความถี่ที่แน่นอนของคุณ อาจเป็นได้ว่าคุณน่าสนใจ
Fat32

คำตอบ:


5

อัลกอริทึมแรกที่น้ำพุใจเป็นGoertzel อัลกอริทึม อัลกอริทึมนั้นจะถือว่าความถี่ของความสนใจเป็นจำนวนเต็มคูณของความถี่พื้นฐาน อย่างไรก็ตามบทความนี้ใช้อัลกอริทึม (ทั่วไป) กับกรณีที่คุณสนใจ


ปัญหาอื่นคือโมเดลสัญญาณไม่ถูกต้อง 2*%pi*(1:siglen)*(Fc/siglen)มันใช้ มันควรใช้2*%pi*(0:siglen-1)*(Fc/siglen)สำหรับเฟสที่จะออกมาอย่างถูกต้อง

ฉันคิดว่ามีปัญหากับความถี่Fc=21.3ที่ต่ำมาก สัญญาณที่มีมูลค่าจริงความถี่ต่ำมักจะมีอคติเมื่อเกิดปัญหาการประมาณเฟส / ความถี่

ฉันลองค้นหากริดแบบหยาบสำหรับการประมาณเฟสและให้คำตอบเดียวกับอัลกอริทึมของ Goertzel

ด้านล่างคือโครงเรื่องที่แสดงอคติในการประมาณค่าทั้งสอง (Goertzel: น้ำเงิน, หยาบ: แดง) สำหรับสองความถี่ที่แตกต่างกัน: Fc=21.3(ทึบ) และFc=210.3(ประ) อย่างที่คุณเห็นอคติสำหรับความถี่สูงนั้นน้อยกว่ามาก

พล็อตแกนเป็นขั้นตอนเริ่มต้นของการเปลี่ยนแปลงที่จะ 0 จาก22 πx2π

ป้อนคำอธิบายรูปภาพที่นี่


เพิ่งทดสอบรหัสสำหรับอัลกอริทึม Goerzel ตามกระดาษ ด้วยการใช้ค่าเอาต์พุต DTFT สามารถหาจุดสูงสุดได้อย่างแม่นยำมาก อย่างไรก็ตามมีปัจจัยการปรับมาตราส่วนที่ 1,000 จุดดังนั้นหากจุดสูงสุดดั้งเดิมคือ 1,234 หลังจาก Goerzel จะเป็น 1234 ใครรู้หรือไม่ว่าสิ่งนี้มาจากไหน
lR8n6i

ได้ทำการวิจัยบ้างในระหว่างนี้ อาจเป็นเรื่องเกี่ยวกับการขยายขนาดแอมพลิจูด: การปรับสเกลเวลาโดเมนแอมพลิจูด = สัมประสิทธิ์โดเมนความถี่ * 2 / N โดยที่ N คือความยาวของสัญญาณ สมมติฐานนี้ถูกต้องหรือไม่
lR8n6i


Hi! ฉันเพิ่งค้นพบว่าการใช้อัลกอริทึมของ Goertzel แอมพลิจูดที่สัมประสิทธิ์ผลที่ได้นั้นมีความแม่นยำมาก แต่เฟสนั้นผิดทั้งหมด มีใครบางคนมีความคิดที่ว่าสิ่งนี้มาจากไหน? โดย "เฟส" ฉันหมายถึงความล่าช้าเฟสที่ระบุในพื้นฐานของสัญญาณดั้งเดิม
lR8n6i

1
@ Rickson1982 เฟสถูกต้อง คุณแค่ตีความไม่ถูกต้อง :-) โปรดจำไว้ว่า:นั่นคือ (90 องศา) จากสิ่งที่คุณคาดหวัง π/2sin(ω0t+ϕ)j2[ejϕδ~(ω+ω0+2πk)e+jϕδ~(ωω0+2πk)]π/2
Peter K.

4

หากคุณยินดีที่จะใช้ถังขยะ FFT ที่อยู่ใกล้เคียงหลายรายการไม่ใช่แค่ 2 แล้วการแก้ไข Sinc แบบหน้าต่างระหว่างผลลัพธ์ถังขยะที่ซับซ้อนสามารถสร้างการประมาณการที่แม่นยำมากขึ้นอยู่กับความกว้างของหน้าต่าง

การแก้ไขแบบ Windowed Sinc นั้นมักพบได้ในโปรแกรมอัปโหลดเสียงคุณภาพสูงดังนั้นเอกสารในเรื่องนั้นจะมีสูตรการแก้ไขที่เหมาะสมพร้อมการวิเคราะห์ข้อผิดพลาด


ขอบคุณสำหรับความคิดเห็น ฉันจะลองใช้วิธีนี้เช่นกัน
lR8n6i

4

ถ้าคุณใช้ฟลานาแกน [1] มันจะคำนวณจากความแตกต่างของเฟสของเฟสสเปกตรัมต่อเนื่อง Δϕ (ความถี่ในทันที) และถ้าคุณสร้างขนาดขึ้นใหม่โดยใช้ปัจจัยที่ถูกต้อง (ขนาดทันทีทันใด) [2] ใช้ฟังก์ชัน sinc ปกติ: และในตอนท้ายการใช้การแก้ไขพาราโบลารอบ ๆ ขนาดสูงสุดคุณสามารถได้ผลลัพธ์ที่น่าอัศจรรย์วันนี้ฉันคิดว่ามันเป็นวิธีที่ดีที่สุดฉันได้ใช้มันและผลลัพธ์มักจะเสมอ แข็งมาก :-)

sin(πx)(πx)

[1] JL Flanagan และ RM Golden,“ Phase Vocoder,” Bell Technical Technical Journal, vol. 45, pp. 1493–1509, 1966

[2] เคเดรสเลอร์“ การสกัดไซนัสด้วยการใช้ FFT ที่มีความละเอียดหลายระดับ "ใน Proc ภายในวันที่ 9 conf เกี่ยวกับเอฟเฟกต์เสียงดิจิตอล (DAFx-06), มอนทรีออล, แคนาดา, ก.ย. 2549, หน้า 247–252


Hi! ขอบคุณมากสำหรับความคิดเห็นของคุณทั้งหมด ฉันขยายรหัสของฉัน (ดูด้านล่าง) เพื่อรวมตัวกรอง Goertzel เข้ากับการประมาณค่าสูงสุดพาราโบลาเพื่อให้ได้เฟส อย่างไรก็ตามผลลัพธ์ยังไม่ถูกต้อง (+ - 3-4deg) สิ่งนี้ใกล้เคียงกับที่ได้รับหรือมีข้อผิดพลาดในการทำความเข้าใจหรือการเข้ารหัสหรือไม่?
lR8n6i

3

วิธีหนึ่งคือการหาค่าสูงสุดและปรับพาราโบลาให้พอดีจากนั้นใช้ค่าสูงสุดของพาราโบลาเป็นค่าประมาณความถี่และขนาด คุณสามารถอ่านทั้งหมดได้ที่นี่: https://ccrma.stanford.edu/~jos/sasp/Sinusoidal_Peak_Interpolation.html


3

ฉันมีปัญหามากมายกับปัญหาตรงนี้เมื่อสองสามปีก่อน

ฉันโพสต์คำถามนี้:

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

ฉันลงเอยด้วยการคำนวณตั้งแต่เริ่มต้นและโพสต์คำตอบสำหรับคำถามของฉันเอง

ฉันประหลาดใจที่ฉันไม่สามารถค้นหาการแสดงออกที่คล้ายกันบนอินเทอร์เน็ต

ฉันจะโพสต์คำตอบอีกครั้งที่นี่; โปรดทราบว่ารหัสถูกออกแบบมาสำหรับสถานการณ์ที่ฉันซ้อนหน้าต่าง FFT ของฉันด้วย 4x

π


ปริศนานี้ใช้สองปุ่มเพื่อปลดล็อค

กราฟ 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;
}

คุณกำลังแก้ไขความถี่ในขณะที่ OP รู้ความถี่และต้องการแก้ไขแอมพลิจูด
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.3Hz ตัวอย่างที่ 8kHz) นั้นต่ำมาก เนื่องจากสิ่งเหล่านี้เป็นสัญญาณที่มีคุณค่าจริงพวกเขาจะแสดงอคติในการประมาณเฟสสำหรับความถี่ ** ** ใด ๆ

ภาพนี้แสดงให้เห็นว่าพล็อตของอคติ ( phase_est - phase_orig) สำหรับFc = 210.3;(สีแดง) Fc = 21.3;กับอคติสำหรับ อย่างที่คุณเห็นการชดเชยมีความสำคัญมากขึ้นสำหรับ21.3กรณีนี้

ตัวเลือกอื่นคือการลดอัตราการสุ่มตัวอย่างของคุณ เส้นโค้งสีเขียวแสดงอคติสำหรับแทนFs = 8008000

ป้อนคำอธิบายรูปภาพที่นี่


1
ขอบคุณสำหรับการอัพเดท! ดูแผนของฉัน; ฉันยังคิดว่าตัวประมาณเฟสใดจะมีอคติสำหรับความถี่ต่ำนี้ วิธีหนึ่งที่จะหลีกเลี่ยงได้ก็คือการใช้ความถี่ที่รู้จัก (ถ้าทราบ!) เพื่อแก้ไขเฟสการประมาณค่าอคติผ่านตารางค้นหา แต่คุณจะต้องระวัง: อคติจะเปลี่ยนไปตามความถี่ อีกวิธีที่จะทำคือลดอัตราการสุ่มตัวอย่างของคุณ
ปีเตอร์เค

1
ขอบคุณเช่นกัน! อย่างไรก็ตามหากคุณใช้ Fs = 8000 Hz และ Fc = 210 แทนที่จะเป็น 210.3 bias จะดูแย่ลงไปอีก ความคิดใดที่สิ่งนี้มาจากไหน?
lR8n6i

1
Erk! ไม่มีความเห็น. FWIW ประมาณการ Geortzel goertzel = atan(imag(y(2)),real(y(2)))*180/%pi + 90;ไม่มีปัญหา: :-) จะขุดอีกเล็กน้อย ดูพื้นที่นี้
Peter K.

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จะให้อะไรที่สมเหตุสมผลกับคุณ สูตรการแก้ไขคือการประมาณค่าของ AMPLITUDE ของพาราโบลา แต่pกำลังทำการแทรกเฟสซึ่งเป็นเพียง ... คี่
Peter K.

1
ทั้งหมดนี้ก็โอเคยกเว้นที่ตำแหน่งสูงสุด ( p = (yp1 - ym1)/(2*(2*y0 - yp1 - ym1))) จะไม่ถูกต้องในบางครั้งหากคุณใช้ PHASES แทนแอมพลิจูด นี่เป็นเพราะขั้นตอนอาจกระโดดไปรอบ ๆ ขอบเขต +/- 180 องศา สิ่งที่จำเป็นสำหรับการแก้ไขสำหรับเฟสคือการเปลี่ยนบรรทัดนั้นเป็นการp2คำนวณของฉันข้างต้น
Peter K.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.