→ HuangJC : 好吧,或許有,因為我要連做 20次,所以用到了 XD 11/14 04:16
→ hsnuyi : 你有修過DSP跟數值分析/穩定度吧? 11/14 13:59
沒有,
https://zh.wikipedia.org/wiki/%E6%95%B0%E5%80%BC%E7%A8%B3%E5%AE%9A%E6%80%A7
是指這個嗎?
我是學過'用逼進法求解,有可能發散',不知是否指這回事
但還是不明白這裡為什麼要提這件事
好,來談這個問題
首先我雖然是理科的學生,但畢業很久,都還老師了
而且我沒學到的科目,用的可能不是精確的字眼
對我來說,誤差或是頻域取樣的問題
就是同一個問題 :P
那一篇我早看過了,頻率洩漏本來就會發生,那還能指望什麼
再來,從邏輯上我也要說
如果我想表達兩個不同的波型
但我餵給程式的是完全相同的資料
那我能指望傳回的結果有何不同?
舉 1hz 的 sin 波取樣 10點為例
如果我塞入 16 點的陣列裡,後面補 0
和另一種圖形
前面 10/16 是 sin 波,後面 6/16 是 0
但這前面 sin 波部份是一秒!
我的波就是真的長這樣嘛
然後我做 16 點取樣
這樣傳入的陣列資料,不是一模模一樣樣?
所以事實上我可以解釋成
"我正在用一種新的波型來計算,希望算到相近的答案"
答案不可能一樣,波型都變了
--------------
我們來講另一個可能好了,內插法,這同事教我的
方法是:假設輸入波型以 10hz 取樣
(今天我們要談的就是取樣設備有限制,它就是運作在 10hz
這也是沒法子的事,如果可以運作在 16hz 那大家都沒事,事情很簡單)
好,那我就可以取到 10 個點的值
接著,我不要填 10 個值,再補 6 個 0
我用內插法把這 10 個值變成 16 個值,如何?
程式我直接列出
#define M_PI 3.14159265358979323846
#define SAMPLE_RATE 10 // hz
#define FFT_N 16
double sample[SAMPLE_RATE];
void test()
{
fftw_complex *in;
fftw_plan p;
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * FFT_N);
memset(in, 0, sizeof(fftw_complex) * FFT_N);
//fill data
double deltaDeg = 2 * M_PI / SAMPLE_RATE;
int i;
//這裡是 10 個點的取樣
for(i = 0; i < SAMPLE_RATE; i++) {
double x = deltaDeg * i;
sample[i] = sin(x); //很簡單,我只是輸入 sin 波
}
//這裡是把 10 點個變成 16 個點的內插法
for(i = 0; i < FFT_N; i++) {
double x = (double)i / FFT_N * 10;
int x1 = (int)x;
int x2 = x1 + 1;
in[i][0] = sample[x1] * (x - x1) + sample[x2] * (x2 - x);
in[i][1] = 0;
}
p = fftw_plan_dft_1d(FFT_N, in, in, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(p);
fftw_destroy_plan(p);
//read data
TRACE("output:\n");
for(i = 0; i < FFT_N; i++) {
double real = in[i][0];
double imag = in[i][1];
if ( i < FFT_N / 2 )
TRACE("s[%d]=(%f,%f) = %f freq %f\n",
i, real, imag, sqrt(real * real + imag * imag), (double) i );
}
fftw_free(in);
}
※ 編輯: HuangJC (223.138.199.136 臺灣), 11/16/2019 02:47:27
好,請注意到這是用內插法把 10 點取樣擴充成 16 點
完全不是以往學的 補 0
那我們可以讀到什麼結果?
※ 編輯: HuangJC (223.138.199.136 臺灣), 11/16/2019 02:51:09
output:
s[0]=(0.000000,0.000000) = 0.000000 freq 0.000000
s[1]=(1.175571,-7.012620) = 7.110471 freq 1.000000
s[2]=(0.000000,-0.000000) = 0.000000 freq 2.000000
s[3]=(1.175571,-0.376322) = 1.234336 freq 3.000000
s[4]=(0.000000,0.000000) = 0.000000 freq 4.000000
s[5]=(1.175571,0.685697) = 1.360936 freq 5.000000
s[6]=(0.000000,-0.000000) = 0.000000 freq 6.000000
s[7]=(1.175571,1.657851) = 2.032348 freq 7.000000
這結果挺好的
所以,不要再補 0 啦
用內插法好了 XD
還是我附上補 0 的結果?
output:
s[0]=(0.000000,0.000000) = 0.000000 freq 0.000000
s[1]=(4.367883,-1.809236) = 4.727762 freq 0.625000
s[2]=(-2.883839,-2.883839) = 4.078364 freq 1.250000
s[3]=(-0.201906,0.487443) = 0.527605 freq 1.875000
s[4]=(-0.726543,0.000000) = 0.726543 freq 2.500000
s[5]=(-0.072232,-0.174384) = 0.188752 freq 3.125000
s[6]=(-0.193845,0.193845) = 0.274138 freq 3.750000
s[7]=(-0.289519,-0.119923) = 0.313373 freq 4.375000
有覺得比較好嗎?
因為頻率間隔的計算是 10hz/16 = 0.625
所以不會精確的有 1hz 這個讀值,判讀變得很麻煩
必需從最接近的 0.625hz & 1.25 hz 去內插
※ 編輯: HuangJC (223.138.199.136 臺灣), 11/16/2019 02:52:37
其實我還有個大膽的想法
既然 FFT 是運作在週期波上,那我自己把取樣到的值重覆 N 次不就好了?
因為 10hz, 我取樣到 10 個值嘛
那我重覆三次,不就有 30個值?
把它塞入 32個值的陣列裡,不是補的 0 就比較少了?
本來 10 塞入 16 要補 6 個零,有效資料是 10/16 = 63%
現在 30 塞入 32 只補 2 個零,有效資料是 30/32 = 94%
有沒有想過這樣會比較精確?
這是結果:
output:
s[0]=(0.000000,0.000000) = 0.000000 freq 0.000000
s[1]=(0.130241,-0.654765) = 0.667592 freq 0.312500
s[2]=(0.749410,-1.809236) = 1.958303 freq 0.625000
s[3]=(8.080340,-12.093083) = 14.544228 freq 0.937500
s[4]=(-2.883839,2.883839) = 4.078364 freq 1.250000
s[5]=(-1.603337,1.071315) = 1.928317 freq 1.562500
s[6]=(-1.176792,0.487443) = 1.273751 freq 1.875000
s[7]=(-0.920980,0.183194) = 0.939023 freq 2.187500
s[8]=(-0.726543,0.000000) = 0.726543 freq 2.500000
s[9]=(-0.563101,-0.112008) = 0.574133 freq 2.812500
s[10]=(-0.421000,-0.174384) = 0.455687 freq 3.125000
s[11]=(-0.297790,-0.198977) = 0.358149 freq 3.437500
s[12]=(-0.193845,-0.193845) = 0.274138 freq 3.750000
s[13]=(-0.110592,-0.165513) = 0.199060 freq 4.062500
s[14]=(-0.049674,-0.119923) = 0.129803 freq 4.375000
s[15]=(-0.012499,-0.062838) = 0.064069 freq 4.687500
看到最接近的 0.93 那個頻率,這結果還不錯 XD
但是無論如何,不用湊 2的 N次方,還是快樂多了
只有 10個取樣點,那就用 10 個值的陣列
結果會是
output:
s[0]=(0.000000,0.000000) = 0.000000 freq 0.000000
s[1]=(-0.000000,-5.000000) = 5.000000 freq 1.000000
s[2]=(0.000000,-0.000000) = 0.000000 freq 2.000000
s[3]=(0.000000,-0.000000) = 0.000000 freq 3.000000
s[4]=(0.000000,-0.000000) = 0.000000 freq 4.000000
有沒有,精準的有 1hz 那個讀值,而且沒有頻率洩漏
所以我真喜歡這個副程式 XD
麻省理工就是神~
本來快速傅立葉就是為了實用而產生的,就是快
相對的慢速傅立葉(其實沒這個名字;但相對就慢啊)應該慢多了
結果用這東西可以輸入任意 size 的資料
而且還是比其他用快速傅立葉的副程式快了二十倍
又快又好用
※ 編輯: HuangJC (223.138.199.136 臺灣), 11/16/2019 03:12:29
→ HuangJC : 我能用,我同事不能用,因為沒有他的平台副程式.. 11/16 03:13
→ HuangJC : 對了,我的內插還只是線性一階內插.. 11/16 03:17
→ HuangJC : 如果有專屬的 sin 波內插,那可漂亮了 11/16 03:18
→ recorriendo : 一樣的輸入 輸出卻不同 就是你程式設計者本身的問題 11/16 16:10
→ recorriendo : 這不是什麼誤差 而是你不願去了解數學細節 曲解輸出 11/16 16:12
→ recorriendo : 的意義 更不是頻率洩漏 那完全是另一個問題 跟補不 11/16 16:13
→ recorriendo : 補零無關 11/16 16:13
→ recorriendo : 你可以直接補到128點,256點等等 看看得出來結果怎麼 11/16 16:17
→ recorriendo : 變化 或許有助了解其中原理 11/16 16:18
→ wohtp : 對一個isolated, well-defined wave packet來說, 11/18 15:24
→ wohtp : zero padding沒什麼差,反正你真的繼續取樣下去也是 11/18 15:25
→ wohtp : 繼續拿到一串零。 11/18 15:25
→ wohtp : 除此之外,zero padding當然是在惡搞輸入訊號,最後 11/18 15:26
→ wohtp : 你會拿到一樣的結果才有鬼 11/18 15:27
> 反正你真的繼續取樣下去也是繼續拿到一串零。
才不是咧,我在想良好的表達都是要附圖的
最近我有想把圖畫出來講
(反之沒附圖而回應我的人,覺得我能聽懂?聽不懂的話溝通障礙要怪在我身上嗎?)
我不像熟 matlab 的人可以輕輕鬆鬆產出畫面
我都必需一行一行建制完整程式
有時我會想,我已經有自己的解決方案,可以停手了
但既然有人陪我討論,那這熱情值得鼓勵
我想法子生圖就是了
> 除此之外,zero padding當然是在惡搞輸入訊號,最後你會拿到一樣的結果才有鬼
怪的是這就是我要表達的啊,所以你應該懂我在說什麼...
那回過頭來說吧,竟然有人說錯的是我,因為程式是我寫的
Orz.. 而且還不只一個
我相信是溝通上出了問題,唉..
※ 編輯: HuangJC (223.141.233.39 臺灣), 11/20/2019 02:12:04
→ wohtp : 如果你的波形不是一個小包包就不能補零 11/20 02:12
→ wohtp : 差別在哪你自己都已經舉例出來了 11/20 02:14
→ HuangJC : 小包包是指?我聽不懂 11/20 02:29