このソフトに付いては、三上先生が、既に作成されて、発表されています。
(TECHI 「ディジタル信号処理とDSP」p.16 )
前節で、入力信号のデジィタル化 x[n]と、信号処理のインパルスによる考察 h[n」と、出力の関係を
調べました。
私は、これらの関係をビジュアル化して、見たいと思っています。
それに、おもしろい結果もでました。
三上先生の、お作りになった上記のグラフと、同じ結果がでましたが、ちょっと悪さをすると、
結果が違ってみえるのです、ハイ。
上のは、COS波形で作図したもの、下のは、SIN波形で描きました。
このように、見えちゃうんです。
おもしろい...
それに、SIN波とCOS波の、任意の合成波形なんかも、描けちゃいます。
という事は、
フーリエ級数のソフトも、自前で作れることになりますよね。
それから、
ソフト製作のための開発環境は、C++Builder5を、私は使いますが
お持ちでない方は、インタープリタータイプでEXEファイルが作れる
無料のHSP(Hot Soup Processor )
は、如何でしょうか?
(私は、C++Builder5を持っておりますし、HSPは魅力なんですが、簡単らしいけれど、文法を覚えるのがメンドウ.....)
三上先生の本のプログラムを、お手本に、簡単なプログラムを作ります。
windowsの標準の座標系は、以下のように、なっています。(名前は忘れました)
ですので、COS波形が減少する時、図形は増加に、採らないといけません。
画面全体で、1周期を描く時
画面の横幅 widthは、位相では2 になります。
ですから
の関係があります。
factorは、拡大するためのものです。
MAX(
ですから、画面上では、値が小さすぎますから、拡大してやります。
どれ位拡大するかは、好みですが
位で、設定したら、どうでしょうか。
Y座標は
sinが正の時、グラフは、上に上がらないと、いけませんが、
座標系が逆なので、マイナスしてやります。
かくして、P点の、座標は
(X、height/2-factor*sin)
と、なります。
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で結ぶ
}
}
ここで、注意して頂きたいのは
ですので、1KHzでも、1MHzでも、単位は、何でもいいのです。
おもしろいのは
2周期を描こうとするなら
double dots=2*M_PI*f/width; //ピクセルで計算
所を
double dots=2*2*M_PI*f/width; //ピクセルで計算
と、2倍すればよい事です。
ですので
n周期を描こうとすれば
と、n倍するだけで、n周期を描ける所です。
例えば、cosを2周期、sinを3周期描くには
cosのn=2、sinのn=3と、すれば、よいです。
他の部分は、一切手を加えなくても、よいです。
windowsの場合、画面のサイズを変更しても、
このままでは、画面の一部が表示されるだけに、なってしまいます。
これを避けるには、
画面の大きさを変えた時の、windowsのイベント OnResize に、Invalidate を加えてやります。
void __fastcall TForm1::FormResize(TObject *Sender)
{
Invalidate();
}
これだけで、よいのです。
ご覧のように、画面を縮小しても、図形は正しく表示されます、便利やわあ。
先ほどのプログラムの所で
を
と、変えるだけです。
foctor_2は、波の大きさを決める為のものです。
今の場合
なので、
factor_2=Height/(3*(2+3)) 位で、如何でしょう?
お好みで、設定してください。
この波形を、もっと、沢山表示するには
sinとcosの中身の比率 1:3を変えずに、整数倍すれば、
と、以前書きましたので
波形が、もっと沢山表示されます。
画面全体で、1周期のcos波形を描く時
と、計算できました。
では、
画面全体で1周期の時
1周期を表す、ドット数は、widthそのものです。
次に、
画面全体で、n周期のcos波形を描く時は、どうでしょう?
です。
では、この波形の1周期は何ドット? と、言いますと
次の比例式が成り立ちます。
ですので
以上より、
サンプリングされた、Y座標は
まず、
と、定義して
ここで、上式の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);
}
のように、書きます。
と、表示されて、メデタシなんですが、
実は、ごまかしがあります。
画面のwidhtを可変すると
と、正しくありません。
これが、実は、サンプリング間隔が、丸められるために、起こってきます。
どれ位、丸められるかと言いますと、かなりの丸めです。
丸める前のサンプリング間隔が、整数になれば、正しく表示されます。
ウー、頭痛いわ...
今、良策がないものかと、思案中であります。
姑息では、ありますが、 サンプリング間隔が整数になるように、画面の横幅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ずつ増やしました。
実に、姑息な手段ですが、いまんところ、これしか...
三上先生のと、同じだから、合っていると思います。
サンプリング周波数だけは、120位までは、大丈夫です。
エラー処理は、していませんが...
H.15.5.30
This document created by Scientific Notebook 4.1. この文書は次の製品で作成しました Scientific Notebook 4.1.