• ベストアンサー

離散フーリエ変換のプログラムについて

今DFTのプログラムをC言語で書いているんですが、うまく動いてくれません。 DFTの式はX(k)=1/N{Σx(k)*e^(-j2πkn/N)}のシグマの中をn=0からN-1まで足し合わせればいいと思っているのですがちがいますか。 下にプログラムを書きますのでお願いします。 void DFTcore(double x[],int N,double A[],double fai[],double a_rl[],double a_im[]){     x[]はデータ     Nはデータ数     A[]は振幅     fai[]は位相差     a_rl[]は実数部、a_im[]は虚数部 double w,a; int k,n,t; for(k=0;k<N;k++){  //初期化    a_rl[k]=0.0;  //実数部    a_im[k]=0.0;  //虚数部   }     時間間隔は1秒 for(k=0;k<N;k++){       for(n=0;n<N;n++){   w=((2*PI)/N)*k*n;         a_rl[k]=a_rl[k]+x[k]*cos(w);   a_im[k]=-a_im[k]+x[k]*sin(w);  }   a_rl[k]=a_rl[k]/(double)N;実数部   a_im[k]=-a_im[k]/(double)N; 虚数   A[k]=sqrt(a_rl[k]*a_rl[k]+a_im[k]*a_im[k]); //振幅    fai[k]=atan(a_im[k]/a_rl[k]); //位相 } }

質問者が選んだベストアンサー

  • ベストアンサー
  • anisol
  • ベストアンサー率48% (146/301)
回答No.2

正しくは、X(k)=1/N{Σx(n)*e^(-j2πkn/N)} (ただし、k=f*N/f_sample , fは調べたい周波数、f_sampleはサンプリング周波数) です。 とりあえずは、 a_rl[k]=a_rl[k]+x[n]*cos(w); a_im[k]=a_im[k]+x[n]*sin(w); とすれば動作確認はできます。 あとはご自分のプログラムの中でのkの意味(f*N/f_sampleは整数でなくてもよい)、f<(f_sample/2)、に注意し、プログラムを書きなおしてみてください。またきれいなスペクトルを得るには窓関数処理をお忘れなく。 数学、数式が大の苦手の私ですが、DFTはおぼろげに理解できたので、以前N88BASICという言語で(今は知っている人が少ないらしい)、DFTのプログラムを書いたことがあります。なにせクロック周波数12MHzのパソコンだったので、サインテーブルを作って積和演算の部分はマシン語で書いたり、いろいろやりました。 きれいなスペクトルが出ると感動しますよ。ご健闘を祈ります。  

tani92
質問者

お礼

プログラム関する問題は解決しました、ありがとうございました。 しかし実験データから振幅の大きな周波数の周りにそれよりは小さな振幅がでているので窓関数処理をしたほうがよいみたいです。ご指摘ありがとうございました。

その他の回答 (1)

  • motsuan
  • ベストアンサー率40% (54/135)
回答No.1

Σx(t)exp(-i*w*t)/N  (tについての和) をやりたいんだと思うのですが、その場合 一つのwに対してtについての和をとるわけですから a_rl[k]=a_rl[k]+x[k]*cos(w); a_im[k]=-a_im[k]+x[k]*sin(w); で a_rl[]、a_im[]、x[] の [] の中が すべて k になっているのはおかしいですよね。 さらに a_im[k]=-a_im[k]+x[k]*sin(w); だと1回の代入のたびに符号を反転するようになっているので たぶんめちゃくちゃになるでしょう。 a_im[k]=a_im[k]+x[k]*sin(-w)     =a_im[k]-x[k]*sin(w) ということがしたいのではないでしょうか? C++をつかっているようなので STLの<complex>を使ってみてもいいんじゃないでしょうか?

tani92
質問者

お礼

ご指摘ありがとうございます。 どうもnとkの単純な間違いだったみたいなので、お騒がせして申し訳ありませんでした。いまは正常にプログラムは走っていて、これから画像処理のほうに応用していく予定でいき、FFTにも手をだして行きたいと思っています。ありがとうございました。

関連するQ&A

  • 離散フーリエ変換(DFT)のプログラム

    タイトルの通りのレポートを出されたのですが、その問題に似たサンプルソースすら何をいっているのかわからない状態です。ひとまず、サンプルソースが何をいっているのか理解したいので、いくつか教えてください。 ソースです。質問はその後に書かせてもらいました。 #include<stdio.h> #include<math.h> #define N 10 #define F 0.1 #define PI 3.14151692 #define SQR(x) ((x)*(x)) void func() { int n; FILE *fp; fp=fopen("temporal.data","w"); for(n=0;n<N;n++) fprintf(fp,"%lf\n",cos(2.0*PI*F*(double)n)); fclose(fp); } void get_data(double x[]) { int n; FILE *fp; fp=fopen("temporal.data","r"); for(n=0;n<N;n++) fscanf(fp,"%lf",&x[n]); fclose(fp); } void dft(double x[],double X_r[],double X_i[]) { int k,n; for(k=0;k<N;k++){ X_r[k]=X_i[k]=0.0; for(n=0;n<N;n++){ X_r[k]+=x[n]*cos(2.0*PI*(double)n*(double)k/(double)N); X_i[k]-=x[n]*sin(2.0*PI*(double)n*(double)k/(double)N); } } for(k=0;k<N;k++) printf("X[%d]=%lf+j%lf\n",k,X_r[k],X_i[k]); } void amplitude(double X_r[],double X_i[]) { int k; FILE *fp; double amp; fp=fopen("amp.data","w"); for(k=0;k<N;k++){ amp=sqrt(SQR(X_r[k])+SQR(X_i[k])); fprintf(fp,"%lf\n",amp); } fclose(fp); } main() { double x[N],X_r[N],X_i[N]; func(); get_data(x); dft(x,X_r,X_i); amplitude(X_r,X_i); } 文字数の制限があるみたいなので、質問を別にさせてもらいます。

  • 離散フーリエ変換について

    10 REM dft 20 N=8 30 DIM A(N),B(N),X(N) 40 FOR I=0 TO N-1 50 READ X(1) 60 NEXT I 70 P=6.283/N 80 FOR K=0 TO N-1 90 A(K)=0:B(K)=0 100 FOR J=0 TO N-1 110 A(K) =A(K)+X(j)*COS(P*J*K) 120 B(K) =B(K)-X(J)*SIN(P*J*K) 130 NEXT J 140 NEXT K 150 FOR I=0 TO N-1 160 Y=SQL(A(I)^2+B(I)^2) 170 LPRINT I;:LPRINT USING "###,###";A(I),B(I),Y 180 NEXT I 190 DATA 1,1,1,1,0,0,0,0 このDFTプログラムをC言語に直したいのですがよく分かりません; お願いします@@;

  • 離散フーリエ変換のスペクトルについて

    関数f(x)=2sin(πx)をx=0~2まで等間隔1000点でサンプリングし、 離散フーリエ変換 Σ(k=0~N-1) f(k)exp(-2πkni/N) の式から、言語プログラムで計算する式をつくり、1000個の実数Reと虚数Imを得ました。 ピークはもちろん周波数πのときで、スペクトルの値が1000でした。 √(Re^2+Im^2)をスペクトル値、√なしをパワースペクトル値をいうそうですが、元の関数の振幅2とこのスペクトル値とはどのような関係があるのでしょうか? 異なる正弦波を混ぜれば、スペクトル値を見ることによって振幅の比は分かりますが、スペクトル値と振幅には式的になんらかの関係は存在するのでしょうか?  波のエネルギーは振幅の2乗になると思い、2^2=4がスペクトル値としてでる事を期待していましたが途方もなく異なる値が出てしまいました。 どうぞよろしくお願いします。

  • 離散フーリエ変換の対称定理について

    離散フーリエ変換において、 Re[X(k)]=Re[X(N-k)] Im[X(k)]=-Im[X(N-k)] |X(k)|=|X(N-k)| といった対称定理が成り立ちます。 数学的にこれらが成り立つということは、大体理解できたのですが、直感的な意味が分かりません。 僕の理解が正しいとしたら、N=128だとしたら、 N=1の極めて周期の大きな波と、N=127の極めて周期の短い波の、 振幅スペクトル(≒振幅?)が等しいということになると思うのですが、どうしても納得が行きません。 N>64の周波数をフィルターでカットした場合も、対称定理は成り立ち、N>64の周波数の波は出てくる(というか、カットしないと、エイリアシングが生じる)と思うのですが、どういうことなのでしょうか? N>64の周波数の波は、計算上は出てくるが、実際にはそのような波は存在しないのですか? あるいは、N>64の周波数同士の波で、上手く相殺されて、逆離散フーリエ変換をしたときには、影響が出ないということなのですか? また、通常のフーリエ変換では、対称定理は成り立たないと思うのですが、そのことも併せて教えて頂けたらと思います。 よろしくお願いします。

  • 離散フーリエ変換について

    離散フーリエ変換によって得られた値についての質問です。 多くのサイトでその値は Σ(k=0~N-1) f(k)exp(-2πkni/N) という式から求められるとあります。 離散フーリエ変換は本来、ある周期関数が、どのくらいの振幅でどのくらいの周波数の波からできているかを調べるために行うものだと思います。 しかし上記の公式から得られるスペクトル(sqrt(Re^2+Im^2))では振幅の値は得られません。振幅を得るには刻み幅Δ(関数をサンプリングした際の幅)を乗じて Σ(k=0~N-1) f(k)exp(-2πkni/N)*Δ とすれば得られることが分かりました。 最初の公式から得られるスペクトルはなにを表しているのでしょうか?またなぜ刻み幅Δを乗じることで、振幅が求まるのでしょうか? よろしくお願いします。m(__)m

  • フーリエ変換のC言語プログラムについて

    正弦波(およびガウス性雑音)をフーリエ変換(離散)→逆フーリエ変換するというプログラムを組みました。正弦波をフーリエ変換すると実部は2回ピークがくるはずなのですが、すべて「0.000000」または「-0.000000」と表示されてしまいます。虚部は正常なようで実装の仕方もさほど違わないので、何が問題なのかわからずにいます。念のためコードはすべて載せますが、該当箇所は関数Fourierの fp = fopen("reX.txt", "w"); //書き込む あたりです。問題点を教えていただけないでしょうか。お願いします。 //gauss.txt, sin.txt:発生させたガウス性雑音&正弦波 //reX, imX:フーリエ変換の実部と虚部 //re-1, im-1:逆フーリエ変換の実部と虚部 #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #define PI 3.14159265358979323846 #define N 256 //n:長さ, w:角周波数, p:位相(phase), a:振幅 void SinCurve(int n, double w, double p, double a) { FILE *fp; double x; int t; fp = fopen("sin.txt", "w"); //書き込むので"w" if(fp == NULL) { printf("file open error\n"); } else { for(t = 0; t < n; t++) { x = a * sin( w*(double)t + p ); fprintf(fp, "%f\n", x); } } fclose(fp); } //n:長さ, s:分散, m:平均 void Gauss(int n, double s, double m) { FILE *fp; double x, x1, x2, y1; int t; srand((unsigned) time(NULL)); fp = fopen("gauss.txt", "w"); //書き込むので"w" if(fp == NULL) { printf("file open error\n"); } else { for(t = 0; t < n; t++) { x1 = ( (double)rand() + 1.0 ) / ( (double)RAND_MAX + 2.0); x2 = ( (double)rand() + 1.0 ) / ( (double)RAND_MAX + 2.0); y1 = pow(-2.0*log(x1), 0.5) * cos(2.0*PI*x2); y1 = s * y1 + m; fprintf(fp, "%f\n", y1); } } fclose(fp); } //ファイル名sのデータをフーリエ変換し、実部と虚部をreX, imXに保存 void Fourier(int num, char *s) { FILE *fp; int k, n; double largeX, x[N+100], t; fp = fopen(s, "r"); //読み込み if(fp == NULL) { printf("file open error\n"); } else { // printf("%s\n", s); for(k = 0; k < num; k++) { fscanf(fp, "%lf", &x[k]); printf("x[%d]=%f\n", k, x[k]); } } fp = fopen("reX.txt", "w"); //書き込む if(fp == NULL) { printf("file open error\n"); } else { for(k = 0; k < num; k++) { largeX = 0.0; t = 2.0*PI*(double)k / (double)N; for(n = 0; n < num; n++) { largeX += x[n] * cos((double)n*t); // printf("%f\n", largeX); } fprintf(fp, "%f\n", largeX); printf("reX[%d]=%f\n", k, largeX); } } fp = fopen("imX.txt", "w"); //書き込む if(fp == NULL) { printf("file open error\n"); } else { for(k = 0; k < num; k++) { largeX = 0.0; t = 2.0*PI*k / (double)N; for(n = 0; n < num; n++) { largeX -= x[n] * sin(n*t); } fprintf(fp, "%f\n", largeX); } } fclose(fp); } void InverseFourier(int num) { FILE *fp; int k, n; double a[N+100], b[N+100], x, t; //a:reX, b:imX fp = fopen("reX.txt", "r"); //読み込み if(fp == NULL) { printf("file open error\n"); } else { for(k = 0; k < num; k++) { fscanf(fp, "%lf", &a[k]); // printf("a[%d]=%f\n", k, a[k]); } } fp = fopen("imX.txt", "r"); //読み込み if(fp == NULL) { printf("file open error\n"); } else { for(k = 0; k < num; k++) { fscanf(fp, "%lf", &b[k]); // printf("b[%d]=%f\n", k, b[k]); } } fp = fopen("re-1.txt", "w"); //読み込み if(fp == NULL) { printf("file open error\n"); } else { for(n = 0; n < num; n++) { x = 0.0; t = 2.0*PI*(double)n / (double)N; for(k = 0; k < num; k++) { x +=a[k] *cos(k*t) - b[k] *sin(k*t); } x /= (double)N; fprintf(fp, "%f\n", x); // printf("x[%d]=%f\n", n, x); } } /* fp = fopen("im-1.txt", "w"); //読み込み if(fp == NULL) { printf("file open error\n"); } else { for(n = 0; n < num; n++) { x = 0.0; for(k = 0; k < num; k++) { t = 2.0*PI*(double)k / (double)N; x = x + a[k] *sin(n*t) + b[k] *cos(n*t); } x /= (double)N; fprintf(fp, "%f\n", x); } } */ fclose(fp); } int main(void) { SinCurve(N, PI/8.0, 0.0, 1.0); // Gauss(N, 1.0, 0.0); Fourier(N, "sin.txt"); // Fourier(N, "gauss.txt"); InverseFourier(N); return 0; }

  • 離散フーリエ変換について

    double DiscreteFourierTransformClass::DiscreteFourierTransformProcess(int n) { double e=2.7182818; double Pai=3.1415926535; int Tau=10; int N=DataLen/Tau; double Sum=0; for(int k=0;k<N;k++) { Sum+=Tau*Data[k*Tau]*pow(e,i*2*Pai*k*n/N); } return Sum; } Tau*Data[k*Tau]=帯 pow(e,i*2*Pai*k*n/N)=ほしい周波数の乗算 ということで、離散フーリエ展開と同じなのでしょうか? パソコン上で計算するには、離散フーリエ展開で計算して、 FFTというのは、pow(e,i*2*Pai*k*n/N)この回転子が3,4個程度のcos,sinで決まるから その値で計算すれば高速になるよということでしょうか? 離散フーリエ変換 G(n/N)=Σ[k=0::N-1]{τ*f(kτ)e^-i2πkn/N} k:何番目の値か τ:値を読んだ間隔 N:値を読んだ数 n:基本周波数の何倍か G(n/N)->ほしいnの成分を得る

  • 逆離散フーリエ変換

    逆フーリエ変換でもとの波形を作りたいです。 http://www.geocities.jp/supermisosan/fourier.html を参考にしました。 ///参考URLの逆フーリエ変換プログラム #include <stdio.h> #include <math.h> #define max 10000 //Nの最大限度数 #define pi 3.1415926535 //円周率 int main() { int i,k,n,N; double Ref,Imf; double ReF[max+1],ImF[max+1]; FILE *fourier; FILE *inversef; //逆フーリエ変換したいデータをあらかじめfourier.dataに保存しておく fourier=fopen("fourier.data","r"); inversef=fopen("inversef.data","w"); //データの読み込み。 for(N=0;N<max;N++) { if(fscanf(fourier,"%d %lf %lf",&i,&ReF[N],&ImF[N]) == EOF) { N--; break; } } //フーリエ逆変換 for(k=0;k<N;k++) { Ref=Imf=0.0; for(n=0;n<N;n++) { Ref+=(ReF[n]*cos(2*pi*k*n/N)-ImF[n]*sin(2*pi*k*n/N))/N; Imf+=(ReF[n]*sin(2*pi*k*n/N)+ReF[n]*sin(2*pi*k*n/N))/N; } fprintf(inversef,"%d %f %f\n",k,Ref,Imf); } fclose(fourier); fclose(inversef); return 0; } ////// Refが実数部分の値で、Imfが虚数部分の値になるのですが、 どうしたら元の波形データが得られるのかがわかりません。 教えて頂けたら幸いです。

  • 離散フーリエ変換について

    G(n/Nτ)= Σ[k=0->N-1] {τ*f(kτ)*e^(-i2πkn/N)} 上記の式は離散フーリエ変換の式らしいですが、 これを関数化しようとしてつまずいています。 どのように解釈すれば、関数化できるのでしょうか? 特に、複素数iがよくわかりません。 e^iが点を左に90度回転させるくらいはわかります。 e^(-i2πkn/N)を関数powで表現できなくて困っていますが、多分見当違いだとは思います。 フーリエ級数展開はわかります。 最終的にはFFTを行いたいのですが、 その理解の前に離散フーリエ変換ができないといけないと思っています。 よろしくお願いします。 void GraphClass::ScatteredFourierConvert() { /* N-1 G(n/Nτ)= Σ {τ*f(kτ)*e^(-i2πkn/N)} k=0 k=何番目の値か? τ=値を読んだ間隔 N=値を読んだ数 */ int Tau=10; int N=RangeSize.cx*2/Tau; double Sum=0; double e=2.7182818; for(int k=0;k<N-1;k++) { Sum+=Tau*Data[k*Tau]*pow(e,-i*2*PI*k*n/N); } }

  • 離散的

    例えば1KHzで10Vの電圧を試料に通したデータとそのままのデータの二つの波の位相差を測定するとします。 この時のフーリエ変換は  Xa = (1/N)Σ(k=0~N-1) a(k)exp(-jω0・ts・k)  Ya = (1/N)Σ(k=0~N-1) b(k)exp(-jω0・ts・k) N :データの点数(何点取ったか) a(k)、b(k): 各データの電圧の値 ω0: 角速度(2πf) ts:サンプリング時間(1点取るのに1μsなので1ms) そこで試料を通した方をa(k),そのままの方をb(k) とすると Xa-Ya=位相差で出るはずなので虚数をBASICは 使えないのでexpを三角関数にして X1 = (1/N)Σ(k=0~N-1) a(k)cos(ω0・ts・k) X2 = (1/N)Σ(k=0~N-1) a(k)sin(ω0・ts・k) Xa = ATN(X2/X1) この式でXaを出し、Yaも同様にして出して位相差を出したのですが、0.001以下ぐらいしか位相差が出ない・・・ 実際は90°ズレテいるはずなのに・・・ どこがおかしいのでしょうか? だれか教えて下さい。同じ所で3週間以上引っかかっているんですー助けてください。