2006.10.28.
石立  喬

Visual C++ 2005 Express Editionの易しい使い方(11)

--- C++標準のcomplexクラスを用いて水晶振動子の等価回路特性を描く---


  「Visual C++ 2005 Express Editionの易しい使い方(10)」でComplexクラスを自作したが、本来C++に標準で付属しているcomplexクラスも、Visual C++ 2005で使用できる。ここでは、水晶振動子の電気的等価回路にcomplexクラスを応用して、周波数とリアクタンス成分の関係を示すグラフを描く。
 具体的には、複素数で表された二つのインピーダンスの並列計算を行い、複素数インピーダンスの虚数部(リアクタンス成分)を求める。

水晶振動子の等価回路と計算式
 水晶振動子が正確な発振周波数を決めることができるのは、水晶振動子が、特定の極めて狭い周波数範囲のみで大きなL性(インピーダンスの虚数部、すなわちリアクタンスが正)になるからである。水晶振動子がL性になると、それと組み合わせたコンデンサとの間で発振が可能になるので、その特定周波数のみで発振する。
 下図のように等価回路を想定する。左側のLCR直列回路(z1で表す)は水晶の機械的特性を置き換えたもので、右側の並列容量(z2)は浮遊容量である。

   

 したがって、z1およびz2は、下記の式で表される。

   
   
 水晶振動子全体のインピーダンスは、z_xtal=z1*z2/(z1+z2) で求める。Rが非常に小さいので、z_xtalはほとんどリアクタンス成分のみであり、グラフにはリアクタンス成分のみを表示する。

C++標準のcomplexクラスの使い方

◎ヘッダ
  #include <complex> を #pragma once の下に記述する。

◎名前空間
 C++ の標準ライブラリに含まれているので、using namespace std; が必要である。


◎コンストラクタ
   complex<double> z;
   complex<double> z(real,imag);
 などを使用する。

◎複素数の実数部、虚数部を設定する
 zを複素数とすると、z.real(x) で実数部を、z.imag(y) で虚数部を設定することができる。

◎複素数間の演算
 z1、z2、z3を複素数とすると、
   z3=z1+z2;
   z3=z1*z2;
   z1/=z2; (z1=z1/z2 と同じ)
 などが使える。
 インピーダンスz1とインピーダンスz2を並列接続した時のインピーダンスは、
   z3=1.0/(1.0/z1+1.0/z2);
 を変形した、
   z3=z1*z2/(z1+z2);
で求めることができる。

◎実数部、虚数部などのフィールドを取得する
   zを複素数とすると、z.real() で実数部を、z.imag() で虚数部を取得することができる。

プログラムの概要
 1)最上部に、等価回路の各定数を表示する。
 2)座標(X1,Y1)はグラフ描画の基点を示すもので、これを基にしてグラフの升目の線を引き、データの表示を行なう。
 3)z1とz2の実数部は、周波数に依存しないのでforループ外で設定する。z1とz2の並列インピーダンスz_xtal の虚数部は、z_xtal.imag()によって求める。
 4)プロット(計算点)は500個とし、それらを周波数の7.1MHzから7.2MHzの間に割り当てる。プロットがグラフの枠の外に出たときの処置はしていない。

プログラム
 すべての本体プログラムを記述した Form1.h の内容

#pragma once
#include <complex>

using namespace std;    //complexクラスの使用に必要

private: System::Void Form1_Paint(System::Object^ sender,
                  System::Windows::Forms::PaintEventArgs^ e) {

   Graphics^ gr=e->Graphics;

   int X0=170,Y0=10;   //各定数の表示位置
   int X1=90,Y1=50;    //グラフの位置
   int X2=75,Y2=25;    //周波数目盛の位置
   int X3=10,Y3=45;    //インピーダンス目盛の位置

   double INDUCTANCE=1.0E-3;
   double CAPACITANCE_S=0.5E-12;
   double CAPACITANCE_P=50.0E-12;
   double RESISTANCE=0.01;

   int i;
   int x,y,old_x,old_y;

   complex<double> z1,z2,z_xtal;

   double frequency,omega;

   char buf[80];
   String ^ string1;

   System::Drawing::Font^ font1=gcnew System::Drawing::Font("MSゴシック",12);

   //等価回路の各定数を表示
   string1=String::Format("L={0}mH, Cs={1}pF, Cp={2}pF, R={3}Ω",
            INDUCTANCE*1E+3,CAPACITANCE_S*1E+12,CAPACITANCE_P*1E+12,RESISTANCE);
   gr->DrawString(string1,font1,Brushes::Black,X0,Y0);

   //枠を描く
   gr->DrawRectangle(Pens::Black,X1,Y1,500,400);
   //枠内に縦線を引く
   for(i=1;i<10;i++)
      gr->DrawLine(Pens::Gray,X1+i*50,Y1,X1+i*50,Y1+400);
   //枠内に横線を引く
   for(i=1;i<10;i++)
      gr->DrawLine(Pens::Gray,X1,Y1+i*40,X1+500,Y1+i*40);

   //周波数目盛を表示
   string1=String::Format("7.1MHz");
   gr->DrawString(string1,font1,Brushes::Black,X2,Y2);
   string1=String::Format("7.2MHz");
   gr->DrawString(string1,font1,Brushes::Black,X2+500,Y2);

   //インピーダンス目盛を表示
   for(i=0;i<=10;i++){
      sprintf_s(buf,80," %4dkΩ",200-i*40);
      string1=gcnew String(buf);
      gr->DrawString(string1,font1,Brushes::Black,X3,Y3+i*40);
   }

   //グラフを描く
   z1.real(RESISTANCE);  //z1の実数部(R)
   z2.real(0.0);         //z2の実数部()

   for(i=0;i<500;i++){
      frequency=(7.1+i*0.0002)*1E+6;    //7.1MHz から7.2MHz まで200Hz 刻み
      omega=2*Math::PI*frequency;      //ω=2πf  
      z1.imag(omega*INDUCTANCE-1.0/(omega*CAPACITANCE_S));  //z1の虚数部(ωL-1/ωCs)
      z2.imag(-1.0/(omega*CAPACITANCE_P));               //z2の虚数部(-1/ωCp)
      z_xtal=z1*z2/(z1+z2);                            //z1とz2の並列

      if(i==0){
         old_x=X1;
         old_y=Y1+200-(int)(0.001*z_xtal.imag()); 
      }
      else{
         x=X1+i;
         y=Y1+200-(int)(0.001*z_xtal.imag());
         gr->DrawLine(Pens::Red,old_x,old_y,x,y);
         old_x=x;
         old_y=y;
      }
   }

}

得られた画面