プログラミング練習(標本化定理)

1.標本化定理を理解するためのプログラミング

このソフトに付いては、三上先生が、既に作成されて、発表されています。

(TECHI 「ディジタル信号処理とDSP」p.16


dsp4__1.gif

では、何故?

前節で、入力信号のデジィタル化 x[n]と、信号処理のインパルスによる考察 h[n」と、出力の関係を

調べました。

私は、これらの関係をビジュアル化して、見たいと思っています。

それに、おもしろい結果もでました。

三上先生の、お作りになった上記のグラフと、同じ結果がでましたが、ちょっと悪さをすると、

結果が違ってみえるのです、ハイ。

MATH

上のは、COS波形で作図したもの、下のは、SIN波形で描きました。

MATH

このように、見えちゃうんです。

おもしろい...

それに、SIN波とCOS波の、任意の合成波形なんかも、描けちゃいます。

MATH

という事は、

フーリエ級数のソフトも、自前で作れることになりますよね。

それから、

ソフト製作のための開発環境は、C++Builder5を、私は使いますが

お持ちでない方は、インタープリタータイプでEXEファイルが作れる

無料のHSP(Hot Soup Processor

は、如何でしょうか?

(私は、C++Builder5を持っておりますし、HSPは魅力なんですが、簡単らしいけれど、文法を覚えるのがメンドウ.....)

2.先ずは、COS波形を1周期分表示

三上先生の本のプログラムを、お手本に、簡単なプログラムを作ります。

windowsの標準の座標系は、以下のように、なっています。(名前は忘れました)

MATH

ですので、COS波形が減少する時、図形は増加に、採らないといけません。

MATH

画面全体で、1周期を描く時

画面の横幅 widthは、位相では2$\pi $ になります。

ですから

1ドット=MATH

の関係があります。

factorは、拡大するためのものです。

MAX(MATH

ですから、画面上では、値が小さすぎますから、拡大してやります。

どれ位拡大するかは、好みですが

MATH

位で、設定したら、どうでしょうか。

Y座標は

sin$\theta $が正の時、グラフは、上に上がらないと、いけませんが、

座標系が逆なので、マイナスしてやります。

かくして、P点の、座標は

(X、height/2-factor*sin$\theta $)

と、なります。

X座標は、1ドットずつ、動かす訳です。

C++Builder5では、こんな感じです。

__fastcall TForm1::FormPaint(TObject *Sender)

{ int x0=0;

int y0=Form1->Height/2;

int y=0,y2=0;

int factor=Form1->Height/4,

int j=0,x=0;

int width=Form1->Width;

double dots=2*M_PI*f/width; //ピクセルで計算

Form1->Canvas->Pen->Color=clAqua;

Form1->Canvas->Pen->Width=2;

for(j=0;j<width;j++)

{

y =cos(dots*j)*factor;

y2=cos( dots*(j+1) )*factor;

Form1->Canvas->MoveTo(x0+j,y0-y); //ラインを描くには、MoveToで(x0+j,y0-y)まで動き、

Form1->Canvas->LineTo(x0+j+1,y0-y2) ; //次の点とを、LineToで結ぶ

}

MATH

$\vspace{1pt}$ここで、注意して頂きたいのは

X軸の目盛りは、ない事です。

ですので、1KHzでも、1MHzでも、単位は、何でもいいのです。

要は、何周期分、表示するかだけ、なのです。

おもしろいのは

$\qquad $2周期を描こうとするなら

$\qquad $double dots=2*M_PI*f/width; //ピクセルで計算

$\qquad \quad $所を

$\qquad $double dots=2*2*M_PI*f/width; //ピクセルで計算

$\vspace{1pt}$

と、2倍すればよい事です。

ですので

n周期を描こうとすれば

double dots=2 x n x M_PI*f/width;

と、n倍するだけで、n周期を描ける所です。

例えば、cosを2周期、sinを3周期描くには

cosのn=2、sinのn=3と、すれば、よいです。

他の部分は、一切手を加えなくても、よいです。

MATH

windowsの場合、画面のサイズを変更しても、

このままでは、画面の一部が表示されるだけに、なってしまいます。

MATH

これを避けるには、

画面の大きさを変えた時の、windowsのイベント OnResize に、Invalidate を加えてやります。

void __fastcall TForm1::FormResize(TObject *Sender)

{

Invalidate();

}

これだけで、よいのです。

MATH

ご覧のように、画面を縮小しても、図形は正しく表示されます、便利やわあ。

2. 2MATH

先ほどのプログラムの所で

y =cos(dots*j)*factor;

y ={2*cos(dots*j)+3*sin(3*dots*j}*factor_2;

と、変えるだけです。

cosを2倍して、sinの中身のdotsを3倍し、sin全体を3倍し、更に、全体に、factor_2を掛ければよいです。

foctor_2は、波の大きさを決める為のものです。

今の場合

Max( 2*\cos\+3*\cos\)=5

なので、

factor_2=Height/(3*(2+3)) 位で、如何でしょう?

お好みで、設定してください。

MATH

この波形を、もっと、沢山表示するには

sinとcosの中身の比率 1:3を変えずに、整数倍すれば、

\lbrack double dots=2 x n x M_PI*f/width;

と、n倍するだけで、n周期を描ける所です。]

と、以前書きましたので

波形が、もっと沢山表示されます。

n1=2;

n2=3;

y =(n1*cos(3*delta_t*j) +n2*cos(9*delta_t*j))*factor_2;

y2=(n1*cos(3*delta_t*(j+1))+n2*cos(9*delta_t*(j+1)))*factor_2;

Form1->Canvas->MoveTo(x0+j,y0-y);

Form1->Canvas->LineTo(x0+j+1,y0-y2) ;

MATH

$\vspace{1pt}\quad $

MATH

$\vspace{1pt}$3.サンプリングをするには

画面全体で、1周期のcos波形を描く時

1ドット=MATH

と、計算できました。

では、

画面全体で1周期の時

1周期を表す、ドット数は、widthそのものです。

次に、

画面全体で、n周期のcos波形を描く時は、どうでしょう?

1ドット=MATH

です。

では、この波形の1周期は何ドット? と、言いますと

次の比例式が成り立ちます。

1 ドット : X ドット = MATH $\U{ff1a} 2\pi $

ですので

MATH

(このXは、整数でない時があります。

座標ですから、整数に丸められてしまいます。

これが、問題を引き起こすのですが、後ほど...)

以上より、

サンプリングされるcos波は、1ドット毎に描き、

サンプリング間隔は、MATH ドット毎であります。

サンプリングされた、Y座標は

まず、

double dots_for_sampling=show_width/fs;

と、定義して

y =cos(2*M_PI*f*j/show_width)*factor; (c++Builderでは、$\pi =$M_PIと書きます。)

ここで、上式のfは、サンプリングされるほうの、周波数で,fsはサンプリング周波数です。

繰り返しの for文を使って、例えば

for(j=0;j<=show_width;j+=dots_for_sampling)

{

y =cos(2*M_PI*f*j/show_width)*factor;

x=x0+j;

Form1->Canvas->Pen->Width=3;

Form1->Canvas->MoveTo(x,y0-y);

Form1->Canvas->Ellipse(x-2,y0-y-2,x+2,y0-y+2) ;

Form1->Canvas->Pen->Width=1;

Form1->Canvas->MoveTo(x_former,y_former);

if(x!=x0) Form1->Canvas->LineTo(x,y0-y);

}

のように、書きます。

MATH

と、表示されて、メデタシなんですが、

実は、ごまかしがあります。

画面のwidhtを可変すると

MATH

と、正しくありません。

これが、実は、サンプリング間隔が、丸められるために、起こってきます。

どれ位、丸められるかと言いますと、かなりの丸めです。

MATH

丸める前のサンプリング間隔が、整数になれば、正しく表示されます。

MATH

ウー、頭痛いわ...

今、良策がないものかと、思案中であります。

2.で、解決策は、こうなりました。

姑息では、ありますが、 サンプリング間隔が整数になるように、画面の横幅widthを計算させて

可変にしました...

自動で計算させます。

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Form1->ClientWidth=600 ;

do

{

if( Form1->ClientWidth<=960)Form1->ClientWidth+=1;

else

Form1->ClientWidth=400;

 

}

 

while ( (sampling_dots/(int)sampling_dots) !=1 );

 

 

show_width=Form1->ClientWidth;

dots=2*M_PI*f/show_width;

 

}

要は、サンプリング間隔が、整数になるまで、widthを1ずつ増やしました。

実に、姑息な手段ですが、いまんところ、これしか...

MATH

MATH

三上先生のと、同じだから、合っていると思います。

MATH

$\quad $

MATH

サンプリング周波数だけは、120位までは、大丈夫です。

エラー処理は、していませんが...

MATH

$\quad $

H.15.5.30

This document created by Scientific Notebook 4.1. この文書は次の製品で作成しました Scientific Notebook 4.1.