2008.1.23.
石立 喬
Visual C++ 2005 Express Edition の易しい使い方(9)
――― リサージュ波形を画面に表示させる ―――
リサージュ(Lissajous)波形は、電気技術者にとって、非常に馴染みの深い波形である。二つの周波数源の同期や位相を調整するのに便利で、オッシロスコープを学ぶ際に、必ずと言っても良いほど実験させられる。
リサージュ(Lissajous)波形とは
リサージュ図形、リサージュ曲線などとも呼ばれることがある。フランスの物理学者Jules Antoine Lissajousが、1857年に発表したと言われ、その名前が付けられている。これは、直交するX軸とY軸のそれぞれに異なる正弦波を加えて得られる平面図形のことで、それぞれの正弦波の周波数、位相の違いなどによって、多様な曲線が描かれる。
リサージュ波形による周波数の測定(設定)
オッシロスコープで、基準波を横軸に、被測定波を縦軸に入力すると、リサージュ図形が得られる。上下に描かれた山の数と、左右に描かれた山の数が、基準波と被測定波の周波数比となって現れるので、周波数の測定ができる。リサージュ波形を見ながら被測定波の周波数を調整することにより、周波数を設定することもできる。
リサージュ波形による位相の測定(設定)
同様に、基準波を横軸に、それと同じ周波数の被測定波を縦軸に入力し、縦横が同振幅に表示されるように調節すると、二つの波の位相関係により、直線、円、楕円のリサージュ図形が得られる。位相差を0またはπ(逆相)にすると直線になり、2/πまたは-2/πにすると円になる。この原理により、位相の測定または設定ができる。
パソコン上でのリサージュ波形の作成方法
画面上の座標を(x,y)としたとき、
横軸入力波形 x = A・sin ωt
縦軸入力波形 y = B・sin(ωt + φ)
で描いた図形がリサージュ波形である。
ただし、ここで紹介する例では、A = B とし、tは時間で 0秒から1秒までとする。ωは角周波数で、ω = 2πfの関係がある。fは周波数で、1 Hzから10Hzまでとする。φは縦軸入力波形に付加する位相シフトで、-πラジアンからπラジアンまで変化させる。
プログラムの使用方法
1) プログラムを起動させると、横軸周波数 = 2Hz、縦軸周波数 = 3Hz、位相 = 0の初期設定になっており、その条件でのリサージュ波形が描かれる。
2) 横軸周波数と縦軸周波数は、コンボボックスによって設定可能で、変更すると、直ちにリサージュ波形が描かれる。
3) 縦軸周波数の位相は、スクロールバーで、0.01π単位で設定でき、これも変更と同時にリサージュ波形の描画が行われる。
4) 「ゆっくり」ボタンをクリックすると、その時の設定条件で、各種波形の再描画がゆっくり行われる。
プログラム
注意点は次のとおり。スクロールバーの設定については、文末に別途示す。
1) 時間待ちをさせる Thread::Sleep(10); を使用するために、System::Threading を名前空間として追加する。Sleepのカッコ内は、msecで指定する。
2) 線を引くためには、g->DrawLine(Pan^,int,int,int,int); または g->DrawLine(Pen^,float,float,float,float);
を用いるのが原則である。g->DrawLine(Pan^,double,double,double,double);のように、すべての引数にdoubleを使用すると、オーバーロードできないと言われて、ビルドできない。ただし、g->DrawLine(Pan^,float,double,double,double);のように、引数の一部にfloatやintがある場合には、「doubleからfloatへの変換です。データが失われる可能性があります。」などの警告が出るが、ビルドできる。
3)コンボボックスでは、周波数を1Hzから10Hzまで、1Hz刻みで設定できる。これを、SelectedIndexの0から11に対応させているので、周波数は、SelectedIndex+1で得られる。
using namespace System::Threading;
Boolean slow_flag;
private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e)
{
comboBox1->SelectedIndex=1; //横軸入力波形の周波数を2Hzに初期設定
comboBox2->SelectedIndex=2; //縦軸入力波形の周波数を3Hzに初期設定
slow_flag=false;
}
private: System::Void Form1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^
e) {
Graphics^ g=e->Graphics;
int X0=10,Y0=50; //横軸入力波形用
int X1=190,Y1=50; //縦軸入力波形用
int X2=20,Y2=230; //リサージュ波形用
int SIZE=16; //最小目盛間隔
int t;
double time;
double x,y,x_old,y_old;
int freq_x=comboBox1->SelectedIndex+1;
int freq_y=comboBox2->SelectedIndex+1;
double omega_x=2.0*Math::PI*freq_x;
double omega_y=2.0*Math::PI*freq_y;
double phi1=(hScrollBar1->Value-100)/100.0;
double phi=phi1*Math::PI;
Pen^ pen1=gcnew Pen(Color::LightGreen,1);
//位相シフトを表示する
if(phi1==0.0) label4->Text=" 0";
else if(phi1==1.0) label4->Text=" π";
else if(phi1==-1.0) label4->Text=" -π";
else label4->Text=String::Format("{0}π",phi1);
//グラフの背景を黒にする
g->FillRectangle(Brushes::Black,X0,Y0,10*SIZE,10*SIZE);
g->FillRectangle(Brushes::Black,X1,Y1,10*SIZE,10*SIZE);
g->FillRectangle(Brushes::Black,X2,Y2,20*SIZE,20*SIZE);
//グラフに目盛を入れる
for(int i=0;i<=10;i++){
//横軸入力波形用
g->DrawLine(Pens::LightGray,X0+SIZE*i,Y0,X0+SIZE*i,Y0+10*SIZE);
g->DrawLine(Pens::LightGray,X0,Y0+SIZE*i,X0+10*SIZE,Y0+SIZE*i);
//縦軸入力波形用
g->DrawLine(Pens::LightGray,X1+SIZE*i,Y1,X1+SIZE*i,Y1+10*SIZE);
g->DrawLine(Pens::LightGray,X1,Y1+SIZE*i,X1+10*SIZE,Y1+SIZE*i);
//リサージュ波形用
g->DrawLine(Pens::LightGray,X2+2*SIZE*i,Y2,X2+2*SIZE*i,Y2+20*SIZE);
g->DrawLine(Pens::LightGray,X2,Y2+2*SIZE*i,X2+20*SIZE,Y2+2*SIZE*i);
}
//各種波形を描く
for(t=0;t<=20*SIZE;t++){
time=t/(20.0*SIZE);
x=Math::Sin(omega_x*time);
y=Math::Sin(omega_y*time+phi);
if(t==0){
x_old=x;
y_old=y;
}
else{
g->DrawLine(pen1,X0+t*0.5f-1,Y0+5*SIZE-4*SIZE*x_old,X0+t*0.5f,Y0+5*SIZE-4*SIZE*x);
//横軸入力波形
g->DrawLine(pen1,X1+t*0.5f-1,Y1+5*SIZE-4*SIZE*y_old,X1+t*0.5f,Y1+5*SIZE-4*SIZE*y);
//縦軸入力波形
g->DrawLine(pen1,(float)(X2+10*SIZE+8*SIZE*x_old),Y2+10*SIZE-8*SIZE*y_old,
X2+10*SIZE+8*SIZE*x,Y2+10*SIZE-8*SIZE*y);
//リサージュ波形
x_old=x;
y_old=y;
}
if(slow_flag) Thread::Sleep(10); //10msecだけ待つ
}
slow_flag=false;
}
private: System::Void comboBox1_SelectedIndexChanged(System::Object^ sender,System::EventArgs^
e) {
Invalidate();
}
private: System::Void comboBox2_SelectedIndexChanged(System::Object^ sender,System::EventArgs^
e) {
Invalidate();
}
private: System::Void hScrollBar1_ValueChanged(System::Object^ sender,System::EventArgs^
e) {
Invalidate();
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
slow_flag=true;
Invalidate();
}
得られた画面
図1は、コンボボックスにより、横軸周波数を3Hzに、縦軸周波数を5Hzに設定し、スクロールバーで縦軸位相を0.51πに設定した場合のリサージュ波形を示す。
図1 画面の例、上の山が5個、横の山が3個ある
スクロールバーの設定方法
スクロールバーを用いて-1から0を経て1まで、0.01刻みで変数pを可変にしたいとする。スクロールバーの値(Value)は、正注)の整数に限られるので、スクロールバーで0から200までを設定し、それから100を引いて、100.0で割ればよいと考える。
すなわち、
p=(hSCrollBar1->Value-100)/100.0;
とする。
pの初期値を0にするには、スクロールバーのプロパティでValueを100にしておく。結局、hScrollBar1のプロパティで、
LargeChange --- 10 (規定値のまま)
Maximum ------- 209
Minimum ------- 0
SmallChange --- 1 (規定値のまま)
Value --------- 100
とする。Maximumを200ではなく、209としたのは、つまみ部分の幅を加味したためである。
注:負に設定して -100から100までとして(Minimumを -100、Valueを0とする)ビルドし、実行することもできたが、不安定で、時々エラーが出ることがあったので、使わない方が良いと判断した。