หากคุณมีฟังก์ชั่นและการอ้างอิงคลื่นของคลื่นอะไรจะเป็นวิธีที่รวดเร็วในการคำนวณ ?
ฉันดูอัลกอริธึมของGoertzelแต่ดูเหมือนจะไม่เกี่ยวข้องกับเฟสเลยใช่ไหม
หากคุณมีฟังก์ชั่นและการอ้างอิงคลื่นของคลื่นอะไรจะเป็นวิธีที่รวดเร็วในการคำนวณ ?
ฉันดูอัลกอริธึมของGoertzelแต่ดูเหมือนจะไม่เกี่ยวข้องกับเฟสเลยใช่ไหม
คำตอบ:
ใช้ DFT ที่ความถี่เฉพาะ จากนั้นคำนวณแอมพลิจูดและเฟสจากชิ้นส่วนจริง / จินตนา มันให้ระยะอ้างอิงถึงจุดเริ่มต้นของเวลาการสุ่มตัวอย่าง
ใน 'ปกติ' FFT (หรือ DFT คำนวณสำหรับ N ฮาร์มอนิกทั้งหมด) โดยทั่วไปคุณคำนวณความถี่ด้วย f = k * (sample_rate) / N โดยที่ k เป็นจำนวนเต็ม แม้ว่ามันอาจดูเป็นสิ่งศักดิ์สิทธิ์ (โดยเฉพาะอย่างยิ่งกับสมาชิกของ Church of the Wholly Integer) แต่คุณสามารถใช้ค่าที่ไม่ใช่จำนวนเต็มของ k เมื่อทำ DFT เดี่ยว
ตัวอย่างเช่นสมมติว่าคุณได้สร้าง (หรือรับ) N = 256 คะแนนของคลื่นไซน์ที่ 27 Hz (สมมุติว่า sample_rate = 200) ความถี่ 'ปกติ' ของคุณสำหรับ 256 จุด FFT (หรือ N point 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 ดังแสดงในคะแนนจริงที่สร้างขึ้น (และแอมป์เพิ่มเป็นสองเท่าเพื่อสะท้อนความถี่ pos / neg)
บางสิ่งที่ควรทราบ - ฉันใช้โคไซน์เพื่อสร้างรูปคลื่นทดสอบและตีความผลลัพธ์ - คุณต้องระวังเกี่ยวกับ - เฟสนั้นอ้างอิงถึงเวลา = 0 ซึ่งก็คือตอนที่คุณเริ่มการสุ่มตัวอย่าง (เช่น: เมื่อคุณรวบรวม r [0] ) และโคไซน์คือการตีความที่ถูกต้อง)
โค้ดด้านบนไม่สวยงามหรือมีประสิทธิภาพ (เช่น: ใช้ตารางการค้นหาสำหรับค่า sin / cos ฯลฯ )
ผลลัพธ์ของคุณจะแม่นยำยิ่งขึ้นเมื่อคุณใช้ N ที่ใหญ่กว่าและมีข้อผิดพลาดเล็กน้อยเนื่องจากความจริงที่ว่าอัตราตัวอย่างและ N ด้านบนนั้นไม่ได้ทวีคูณซึ่งกันและกัน
แน่นอนถ้าคุณต้องการเปลี่ยนอัตราตัวอย่าง N หรือ f คุณต้องเปลี่ยนรหัสและค่า k คุณสามารถทิ้งลงถังขยะ DFT ที่ใดก็ได้บนเส้นความถี่ต่อเนื่อง - เพียงตรวจสอบให้แน่ใจว่าคุณกำลังใช้ค่า k ที่สอดคล้องกับความถี่ที่น่าสนใจ
ปัญหาสามารถกำหนดเป็นปัญหาน้อยที่สุด (ไม่เชิงเส้น):
ที่เป็นฟังก์ชันวัตถุประสงค์เพื่อลดส่วนที่เกี่ยวกับ\ϕ
อนุพันธ์นั้นง่ายมาก:
ดังกล่าวข้างต้นฟังก์ชันวัตถุประสงค์สามารถลดซ้ำโดยใช้วิธีการไล่โทนสีโคตร (การสั่งซื้อครั้งแรกประมาณ) วิธีนิวตัน , วิธีเกาส์นิวตันหรือวิธี Levenberg-Marquardt (ลำดับที่สองประมาณ -จะต้องมีการระบุไว้ในเหล่านี้)
เห็นได้ชัดว่าฟังก์ชันวัตถุประสงค์ข้างต้นมีหลายค่าน้อยที่สุดเนื่องจากเป็นช่วงเวลาดังนั้นจึงมีการเพิ่มเงื่อนไขการลงโทษเพื่อแยกแยะความแตกต่างของขนาดเล็ก (ตัวอย่างเช่นการเพิ่มเข้ากับสมการโมเดล แต่ผมคิดว่าการเพิ่มประสิทธิภาพก็จะมาบรรจบกับน้อยที่ใกล้ที่สุดและคุณสามารถปรับปรุงผลลบN 2 π k , k ∈ N
มีสูตรที่แตกต่างกันหลายอย่างของอัลกอริทึม Goertzel คนที่มี 2 รัฐตัวแปร (orthogonal หรือใกล้เคียง) หรือตัวแปรรัฐที่ซับซ้อนที่สุดเท่าที่จะเป็นไปได้เอาท์พุทมักจะถูกนำมาใช้เพื่อคำนวณ คนที่ให้ผลลัพธ์สเกลาร์เดียวอย่างเดียวมักจะไม่สามารถทำได้
คุณจะต้องทราบว่าหน้าต่าง Goertzel ของคุณเกี่ยวข้องกับแกนเวลาของคุณอย่างไร
หากสัญญาณของคุณไม่ใช่จำนวนเต็มเป็นระยะในหน้าต่าง Goertzel ของคุณเฟสจะประมาณรอบจุดอ้างอิงที่อยู่ตรงกลางของหน้าต่างอาจมีความแม่นยำมากขึ้นดังนั้นการอ้างอิงเฟสถึงจุดเริ่มต้นหรือจุดสิ้นสุด
FFT เต็มจะ overkill ถ้าคุณรู้ความถี่ของสัญญาณของคุณ บวก Goertzel สามารถปรับให้เป็นความถี่ที่ไม่ได้เป็นระยะในความยาว FFT ในขณะที่ FFT จะต้องมีการแก้ไขเพิ่มเติมหรือเป็นศูนย์รองสำหรับความถี่ที่ไม่ใช่ระยะในหน้าต่าง
Goertzel ที่ซับซ้อนเทียบเท่ากับ 1 bin ของ DFT ที่ใช้การเกิดซ้ำสำหรับเวกเตอร์พื้นฐานไซน์และไซน์หรือปัจจัย FFT twiddle
หากสัญญาณของคุณปราศจากสัญญาณรบกวนคุณสามารถระบุการข้ามศูนย์ทั้งในและกำหนดความถี่และเฟสสัมพัทธ์
ขึ้นอยู่กับความหมายของคำว่า "เร็ว" ความแม่นยำที่คุณต้องการประมาณการของคุณไม่ว่าคุณต้องการหรือเฟสที่สัมพันธ์กับตัวอย่างของคุณ
วิธีหนึ่งที่จะทำเช่นนี้คือการใช้เวลาเพียงแค่ FFT ของและดูเพียงแค่ในถังที่ใกล้เคียงกับ\โอห์ม อย่างไรก็ตามสิ่งนี้จะขึ้นอยู่กับที่อยู่ใกล้กับความถี่ศูนย์ช่องเก็บ
ดังนั้น:
PS: ฉันสมมติว่าคุณหมายถึงมากกว่าφ)
จุดเริ่มต้น:
1) ทวีคูณสัญญาณและการอ้างอิงคลื่นบาปของคุณ: = A⋅sin (ωt + ϕ) ⋅sin (ωt) = 0.5⋅A⋅ (cos (ϕ) - cos (2⋅ωt + ϕ) )
2) ค้นหาอินทิกรัลตามช่วงเวลา :
3) คุณสามารถคำนวณ :
T = π / ω ฉัน( φ ) = ∫ T 0 F ( T ) d T = 0.5 ⋅ ⋅ คo s ( φ ) ⋅ T φ คo s ( φ ) = ฉัน( T ) / ( 0.5 ⋅ A ⋅ T )
คิดอย่างไร:
จะวัดได้อย่างไร
วิธีการตรวจสอบในช่วงเวลา? (คิดเกี่ยวกับ "การอ้างอิงคลื่นคอส ")0 .. ( 2 ⋅ เธ)
สำหรับสัญญาณไม่ต่อเนื่องเปลี่ยนอินทิกรัลเป็นผลรวมและเลือก T!
คุณสามารถทำเช่นนี้ (ในสัญกรณ์ numpy):
np.arctan( (signal*cos).sum() / (signal*sin).sum() ))
โดยที่สัญญาณคือสัญญาณที่เปลี่ยนเฟสของคุณ cos และ sin เป็นสัญญาณอ้างอิงและคุณสร้างการประมาณของอินทิกรัลในช่วงเวลาหนึ่งผ่านการสรุปผลรวมของผลิตภัณฑ์ทั้งสอง
นี่คือการปรับปรุงคำแนะนำของ @Kevin McGee เพื่อใช้ DFT ความถี่เดียวกับดัชนีถังเศษส่วน อัลกอริทึมของ Kevin ไม่ได้ผลลัพธ์ที่ยอดเยี่ยม: ในขณะที่ครึ่งถังขยะและถังขยะทั้งหมดนั้นแม่นยำมากใกล้กับ wholes และครึ่งหนึ่งก็ค่อนข้างดี แต่ข้อผิดพลาดอาจอยู่ใน 5% ซึ่งอาจไม่เป็นที่ยอมรับสำหรับงานส่วนใหญ่ .
ฉันแนะนำให้ปรับปรุงอัลกอริทึมของ Kevin โดยปรับนั่นคือความยาวของหน้าต่าง DFT เพื่อให้ใกล้เคียงกับภาพรวมมากที่สุด การทำงานนี้แตกต่างจาก FFT DFT ไม่ต้องการให้เป็นพลังของ 2
รหัสด้านล่างเป็นแบบ 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))