DFT(Discrete Fourier Transform)(gnuplot による画像保存)

前回の popen の例題を利用し,gnuplot によりDFTの処理結果をグラフ化・画像保存する例題です.

今回の例題では,
y = sin(x) + sin(5x) + sin(50x)
で定義される信号を出力した入力ファイル(signal.txt)を準備し,その入力のスペクトルをDFTにより計算しています.スペクトルの計算結果では,3つの正弦波の入力によるスペクトルが表示されています.

DFT(Discrete Fourier Transform)(gnuplot による画像保存) (list_80.c)
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define STDIN  "<stdin>"
#define STDOUT "<stdout>"
#define TMPIN  ".in"
#define TMPOUT ".out"
#define N 2048 // 最大データ数
#ifdef M_PI
#define PI M_PI
#else
#define PI 3.14159265358979323846
#endif
#define Deg2Rad(x) (PI*x/180.)

//-----------------------------------------------------------------
void usage(char* cmd);
int file_data(char* in_file,double *x);
void out_data(char* out_file,double* psd,int data_num);
char* setgname(char* out_file);
void gnu_output(char* in_file,char* out_file);
void dft_spectrum(double *y, double *x, int data_num);
//-----------------------------------------------------------------

int main(int argc,char* argv[])
{
    int i,data_num;
    int use_simdata = 0,out_graph = 0;
    char *in_file = STDIN,*out_file = STDOUT;
    double x[N],psd[N/2];

    // read argument
    for(i=1;i<argc;i++){
        if(argv[i][0] == '-'){
            switch(argv[i][1]){
            case 'h': usage(argv[0]); return 0;
            case 'o':
                i++;
                if(argv[i] == NULL){
                    fprintf(stderr,"Output file not specified.\n");
                    usage(argv[0]);
                    return 0;
                }
                out_file = argv[i];
                break;
            case 'd':
                use_simdata = 1;
                break;
            case 'g':
                out_graph = 1;
                break;
            default:
                fprintf(stderr,"Unknown option '%c'\n",argv[i][1]);
                usage(argv[0]);
                return 0;
            }
        } else {
            in_file = argv[i];
        }
    }

    if(use_simdata) data_num = sim_data(x);
    else            data_num = file_data(in_file,x);
    if(data_num == 0) return;
    if(data_num)    dft_spectrum(x,psd,data_num);

    if(out_graph){
        if(!strcmp(in_file,STDIN)){
            in_file = TMPIN;
            out_data(in_file,x,data_num);
        }
        if(!strcmp(out_file,STDOUT))
            out_file = TMPOUT;
        out_data(out_file,psd,data_num/2);
        gnu_output(in_file,out_file);
        if(!strcmp(in_file,TMPIN))   remove(TMPIN);
        if(!strcmp(out_file,TMPOUT)) remove(TMPOUT);
    } else {
        out_data(out_file,psd,data_num/2);
    }
    
    return 0;
}

void usage(char* cmd)
{
    printf("usage: %s in_file [options]\n",cmd);
    printf("option:\n");
    printf("\t-h: display this information\n");
    printf("\t-o out_file: output to file.\n");
    printf("\t-d: use simulation data.\n");
    printf("\t-g: output to graph\n");
}

// ファイルのデータを格納する
int file_data(char* in_file,double *x)
{
    FILE* fp = stdin;
    int data_num = 0;
    char line[BUFSIZ];

    if(strcmp(in_file,STDIN))
        fp = fopen(in_file,"r");
    else
        printf("** input data (Ctrl-D end.)\n");
    if(fp == NULL) return 0;
    
    // ファイルからデータの読み取り
    while(fgets(line,BUFSIZ,fp)){
        sscanf(line,"%lf",&x[data_num]);
        data_num++;
        if(data_num > N){
            fprintf(stderr,"!!input data exceeded max read size\n");
            data_num = 0;
            break;
        }
    }
    fclose(fp);
    
    // データ数を返す
    return data_num;
}

// データを出力する
void out_data(char* out_file,double* psd,int data_num)
{
    FILE* fp = stdout;
    int k;
    if(strcmp(out_file,STDOUT))
        fp = fopen(out_file,"w+");
    else
        printf("** power spectrum\n");
    if(fp == NULL) return;
    
    for(k=0;k<data_num;k++)
        fprintf(fp,"%lf %lf\n",k/(double)data_num,psd[k]);
    fclose(fp);
}

// シミュレーションデータの作成
int sim_data(double* x)
{
    int k,data_num = N;
    for(k=0;k<N;k++){
        x[k] = sin(Deg2Rad(k)) + sin(Deg2Rad(5*k)) + sin(Deg2Rad(50*k));
        printf("%lf\n",x[k]);
    }
    return data_num;
}

char* setgname(char* out_file)
{
    int i;
    char* ofile = out_file;
    static char gname[256];
    if(!strcmp(out_file,TMPOUT))
        ofile = "output.png";
    for(i=strlen(ofile)-1;i>=0;i--){
        if(ofile[i]=='.') break;
    }
    if(i==-1) i = strlen(ofile);
    strncpy(gname,ofile,i);
    strcat(gname,".png");
    return gname;
}

// グラフ出力
void gnu_output(char* in_file,char* out_file)
{
    FILE* gfp = popen("gnuplot","w");
    fprintf(gfp,"set term png\n");
    fprintf(gfp,"set output \"%s\"\n",setgname(out_file));
    fprintf(gfp,"set multiplot\n");
    fprintf(gfp,"set size 1.0, 0.5\n");
    fprintf(gfp,"set origin 0, 0.5\n");
    fprintf(gfp,"plot \"%s\" w l 1\n",in_file);
    fprintf(gfp,"set origin 0, 0\n");
    fprintf(gfp,"plot \"%s\" t \"Power Spectrum by DFT\" w l 2\n",out_file);
    fflush(gfp);
    pclose(gfp);
}

// 離散フーリエ変換(Discrete Fourier Transform)
void dft_spectrum(double *x,double *psd,int data_num)
{
    int k,n;
    double Xr = 0.0,Xi = 0.0;
    // DFT
    for(k=0;k<data_num/2;k++){
        for(n=1;n<data_num;n++){
            Xr += x[n] * cos(2*PI*k*n/data_num);
            Xi += x[n] * sin(2*PI*k*n/data_num);
        }
        psd[k] = pow(Xr,2) + pow(Xi,2);
        Xr = 0.0; Xi = 0.0;
    }
}

実行結果
Gami[1336]% ./dft.exe -h
usage: ./dft in_file [options]
option:
        -h: display this information
        -o out_file: output to file.
        -d: use simulation data.
        -g: output to graph
Gami[1337]% ./dft.exe signal.txt -o output.txt -g
Gami[1338]%
inserted by FC2 system