jl7gmnのblog

yahooブログから移行してきました。アマチュア無線を中心としたブログです。

vbausbio.dllをVisual C++2010 その10

イメージ 1

イメージ 2

シリアル通信による八重洲無線機FT-1000MPの周波数を読み取るプログラムにトライしてみた。とにかくVisualC++2010Expressは型変換については厳しい。単純にいって、私の場合は型変換エラーがほとんどを占めています。とは言っても学習しながらだからしょうがないか?。無線機の周波数を読み取るためのデータのやり取りは基本的にバイトデータにて処理を行っています。このためバイトデータから他の型への変換がかなりあります。桁処理もあるし、このため型変換で使うarray配列もかなり使用することになりました。VisualBasic2008Expressのように単純にString配列のみですべてコーディングとはいかない。とにかく今まで出したエラーは型変換が一致していないためのエラーがほとんどだ。がしかし一度変換方法がうまく設定できると後は、応用ができる。ここまでくる過程が実は一番楽しいところである。前置きはこれぐらいにして、本題だが、基本はモード変更と同じくパラメータとコマンドをRS-2323Cで無線機に送ることが最初のステップである。FT-1000MPの場合はCATコントロールコマンド表の中で言うと、No.14のコンファーム・リクエストである。
10,P1, ※ , ※ ,P4
がコマンドとパラメータ構成で表示データ読み出しなのでP1が02、P4はにメモリ読み込みは使わないので 00にする。
10,02,00,00,00
が対応するパラメータコマンドとなる。まずはこれを今回は関数化したft-1000mpcomを使わずに直接書いてゆきます。これは周波数読み取りのコマンドボタンに対応させますが、タイマーコントロールを用意して、このボタンをコールし、タイマーインターバル間隔で連続読み取りにしてゆきます。
データは上記のcntcomdat="1002000000"をセットし、各シリアルポート条件を設定しポートを開きます。その後送るデータを加工して、_serialPort1->Write(kako,0,comlength);で送出します。ここからがシリアルポートへのコンファーム・リクエストコマンドによるデータの読み出し処理となります。最初のトラブルは、次の一行でデータを一回で読み取れると思っていたのですが、デバッグ状態ではうまく全部読み取れますが、ブレークポイントを全てはずすとたった1バイトしか読み出せない状況となりました。原因は不明です。
--------------------------------------------------------------
serialPort1->Read(bin,0,16);
--------------------------------------------------------------
なので上記の方法は止めて、1バイト読みする方法をとりデータ数分のループを行い全部のデータを取り込む方法へ変更しました。なお既定のバッファサイズは4096ですが、今回のデータでは16個(16バイト)なので以下のように設定しループで読み取っています。
--------------------------------------------------------------
int aa=16;
int ac=0;
for(ac=0 ; ac<=aa-1 ;ac++){
bin[ac]=_serialPort1->ReadByte();
}
--------------------------------------------------------------
ここで上記のbin[]にバイトデータで周波数データが16個入ります。
ここまでくれば安心します。周波数のデータかどうかはわかりませんが、データらしきものが取得できているという事が、とてもうれしくなります。
次は上記の読み取ったデータ郡を16進の文字に変換します。変換したデータは1桁と2桁になるため、常に2桁になるように1桁の場合には"0"をつけてあらためて書き直します。ここが意外と何でかという疑問がわいてきますが、周波数の算出にかかわるということがあるために必要な処理ということになります。16バイトのデータ中の1から4までのバイナリーとしての範囲は00027100~02DC6C00(Hex) LSB=0.625Hz(step)となっています。これは10進で160000~48000000(10進)ステップの0.625Hzをかけると 100000~30000000とFT-1000MPの受信周波数範囲単位(Hz)となることより16進の4バイトのデータがフルでいることがわかります。ここで1バイトとは8ビットのことです。16進でたとえば1だとすると2進の4ビットで0001ですが、データとしては1バイト必要ですから実際は上位の4ビットが必要になるわけです。0000と0001で全部で8ビットの1バイトということです。ここで1桁のデータには0をつける必要が出てくるのです。1(hex)のデータは 01(hex)ということです。ということで周波数の計算も上記の各4バイトのデータ値の合計を求めて0.625をかけることで周波数がHz単位で求まることがわかります。つまり2バイト目からのデータが4バイト分周波数データなので、周波数の4ビット毎の各桁の値(10進値)に重みデータにかけて値を出しそれぞれを和算してから0.625をかけることになります。
重みをかける16進値は文字から次の変換で10進化しておきます。以下にここの関係するコードを記します。
-------------------------------------------------------------------
array<String^>^binasc=gcnew array<String^ >(16);
int abc=0;
for (abc=0 ; abc<=aa-1 ;abc++){
binasc[abc]=bin[abc].ToString("X");
//  binasc[abc]=String::Format("{0:x}",bin[abc].ToString("X"));//これもOK
  }
int lenasc;
int abc1=0;
for (abc1=0 ; abc1<=aa-1 ; abc1++){
lenasc=binasc[abc1]->Length;
if( lenasc==1 ){
binasc[abc1]="0" + binasc[abc1];
}else{
binasc[abc1]=binasc[abc1];
}
}
array<String^>^KRE=gcnew array<String^ >(16);
Array::Copy(binasc,KRE,binasc->Length);// 配列をbinascからKREへコピーする
String^ WAKRE;//2桁ずつのデータをすべて連結しいれるための変数

WAKRE = (KRE[0]+KRE[1]+KRE[2]+KRE[3]+KRE[4]+KRE[5]+KRE[6]+KRE[7]+KRE[8]+KRE[9]+KRE[10]+KRE[11]+KRE[12]+KRE[13]+KRE[14]+KRE[15])->ToString();

//周波数計算
array<String^>^LRE=gcnew array<String^ >(32);
array<String^>^NRE=gcnew array<String^ >(32);
//一文字ずつ分けてLRE[31]配列にいれる
int abc2=0;
int ab=32;
for (abc2=0 ; abc2<=ab-1 ;abc2++){
LRE[abc2]=WAKRE->Substring(abc2,1);
}

//値変換10進にもどす
array<int^>^SUCHI=gcnew array<int^ >(32);
int abc3=0;
for (abc3=0 ; abc3<=ab-1 ; abc3++){
SUCHI[abc3]=(Int32::Parse(LRE[abc3], System::Globalization::NumberStyles::HexNumber));
}

int GOUKEI;
int VALSUCHI[10]; //10進化
VALSUCHI[0]=(Int32::Parse(SUCHI[0]->ToString()));// NO USE
VALSUCHI[1]=(Int32::Parse(SUCHI[1]->ToString()));// NO USE
VALSUCHI[2]=(Int32::Parse(SUCHI[2]->ToString()));
VALSUCHI[3]=(Int32::Parse(SUCHI[3]->ToString()));
VALSUCHI[4]=(Int32::Parse(SUCHI[4]->ToString()));
VALSUCHI[5]=(Int32::Parse(SUCHI[5]->ToString()));
VALSUCHI[6]=(Int32::Parse(SUCHI[6]->ToString()));
VALSUCHI[7]=(Int32::Parse(SUCHI[7]->ToString()));
VALSUCHI[8]=(Int32::Parse(SUCHI[8]->ToString()));
VALSUCHI[9]=(Int32::Parse(SUCHI[9]->ToString()));
--------------------------------------------------------------------------------
上記の16バイト中の+0のVALSUCHI[0]とVALSUCHI[1]の1バイト分はBANDNO:+1ら+4の周波数が属するバンドなので今回は使いません。周波数データであるVALSUCHI[2]~VALSUCHI[9]までの4バイトが計算で使う値となります。ほかの16バイト中のデータには+5、+6の2バイトがCLARF、+7の1バイトがモード(0=LSB,1=USB,2=CW,3=AM,4=FM,5=RTTY,6=PKT),+8がFILT(省略)、+9がFLAG、他+Aから+Fまでは未使用と割り当てられています。今回は上記の+1から+4に相当する部分だけ使います。
--------------------------------------------------------------------------------
5000文字制限のためこの続きは次にまわします。つづく

vbausbio.dllをVisual C++2010 その9

イメージ 1

イメージ 2

シリアル通信の無線機へのデータ送出を関数化してみた。使いかってがかなり良くなる。

String^ ft1000mpcom(String^ cntcomdat, String^ retda);

内容はcntcomdatにパラメータとコマンドをretdaには返り値(通常の送りコマンドのみでは"NONE"を入れて送ります。
コマンドボタンには以下の6行を記入するだけでモード変更ができます。
変えるのはcntcomdatのデータのみです。
例)
String^ retda;
String^ cntcomdat;
cntcomdat="0C01000000";
retda="NONE";// コマンド送りのみの場合に"NONE"に設定する
String^ backdata; 
backdata=ft1000mpcom(cntcomdat,retda);

上記の値設定でストリング変数のbackdataにはシリアル通信の受信したデータ(バイトからアスキーデータ変換したデータが入るように組む予定です。現在はretda:"NONE"をそのまま返しています。受信したデータとは、無線機のVFO読み取りデータ値、Sメータ信号のリクエストコマンドによる返り値と内部ステータス状態等を返すデータになると思います。通常のモード変更とかの送りのみのコマンドでは、送り時に設定した"NONE"をそのまま返します。
今現在の構想はハムログに周波数情報と相手のSメータ情報を送ることができたらいいと思っています。Sメータに関してはいろいろと大変そうな感じがあります。常に動いているということと、相手が出ているときに処理しなければならないということがあります。プログレスバーに表示することはいたって簡単ですが、どういうふうにSメータ値データを決定してハムログに送るかを考える必要があります。なおシリアル受信に関してはこの構想で対応する事になります。今現在は、Sメータ値は最大値とするのが手っ取り早いような気がしています。この場合、ノイズでSメータが振れる場合がありますが、まずは実際にやっていってから考えようと思います。まだ受信もできていないのに構想だけは進みます。コマンドを関数化したフォームを載せました。関数化部のコードは先のUSBボタンのコードに受け渡しの変数宣言部の追加部分を載せてプロト宣言した関数をコーディングします。コマンド送出は例のとおりパラメータとコマンドからなるデータを入れ替えてft1000mpcomに入れるだけで簡単に制御できるようになります。今日は本当に暑い。外気温は31.8℃、部屋は30.7℃窓は開けっ放しだが風はなし。パソコンのモニターからは熱が感じられる。そろそろパソコンを止めねば...熱中症には気をつけねば...おわり

vbausbio.dllをVisual C++2010 その8

イメージ 1

イメージ 2

新たに構想をしてみたことを書いてみる。
何かというと現在ハムログはパソコンからシリアルケーブルで無線機に接続されて直接周波数をハムログとの間でやり取りが行われているわけだが、今回はコントロールというか制御を別のプログラムで行い、プログラムが周波数データをハムログへ送るという形式をとるのがいいのではないかと思えた。ハムログと無線機だけの周波数読み取りのみで使用する場合はいいが、他の制御もしてみたい場合は、プログラムが主体になる必要があるためだ。ということで、何をしたいのかといいますと シリアル通信で無線機を制御するということです。遠隔操作にやるにしても直接のパソコン制御にしても無線機には通信機能がついているものがたくさんありますのでこれを利用するわけです。VisualC++2010Expressを用いたシリアル通信をやってみます。すでにVisualBasic2008Expressにて無線機のシリアル制御プログラムはできていますので、これをC++/CLI用に書き換えてみます。
私の使用している無線機はYAESU FT-1000MPです。この無線機はCAT機能用としてRS232C端子を標準で装備しています。また今回のプログラムでの制御コマンドも、テクニカルオーバービューのCATコントロールコマンド表の並びで利用できるようにして組みます。
表表示は コマンド、パラメータの並びで一覧表となっています。例えばMODE(電波型式)の切り換えはUSBモードの場合
コマンド:0C
パラメータ:01、※、※、※
となっています。※は特に関係ないが00を入れます。
コマンド、パラメータの並びは表とおなじですので、コマンドを並べる場合はとても簡単にできます。USBモードの送出データは 次のように準備します。
送出データ:0C、01、00、00、00
これはあくまでもわかりやすくコマンドを使うためです。実際の通信データは並び替えて送出します。実際に送るコードの並びは 00、00,00,01,0Cです。これは通信フォーマットに通信データの構成のなかにパラメータ4、パラメータ3、パラメータ2、パラメータ1、MSDコマンドというふうに書かれています。また通信の設定として
通信速度:4800ビット/秒
データ長:8ビット
スタートビット:1ビット
ストップビット:2ビット
パリティビット:なし
上記を主にプログラムのシリアル通信の設定値として他も含め下記のように初期設定します。ポートは今回は固定COM4(私の都合)で指定しました。
SerialPort^ _serialPort1;
_serialPort1 =gcnew SerialPort();
_serialPort1->PortName = "COM4";
_serialPort1->BaudRate = 4800;
_serialPort1->Parity=System::IO::Ports::Parity::None;
_serialPort1->DataBits=8;
_serialPort1->StopBits= StopBits::Two;
_serialPort1->Handshake = System::IO::Ports::Handshake::None;
_serialPort1->RtsEnable=true;
_serialPort1->Open();
(ここには送受信のコードを書く)
_serialPort1->Close();
通信の終了には必ず上記のClose();を行います。
ここまでは単純な設定ですので、書き方さえ間違わなければ問題はないと思います。実際は少し悩みました(ストップビットの例がOneしかなくで2の場合は数字か、とか実際に試してみてTwoであることがわかりました。)

今回は無線機の制御コマンドの上記のUSBモードにするためのコードをString変数に代入してこれを型変換を用いてシリアル送出用のデータに書き換えてから無線機のシリアルデータラインへ送ることを行います。
各配列を準備します。

array<String^>^indat=gcnew array<String^ >(10); //0から9のindat配列用意されます。
arary<String^>^bun=gcnew array<String^ >(5); // 0から4のbun配列用意されます。
array<Byte>^ kako;
kako=gcnew array<Byte>(5); // 0から4のバイト型kako配列用意されます。

上記の配列はcntcomdatに入れたデータを一旦String配列に入れて、なおかつ2文字ごとにまとめるという変換で使います。

cntcomdatにUSBデータをセットします。
次にデータの長さを求めます。10ですが...
----------------------------------------
String^cntcomdat="0C01000000";
nagasa=cntcomdat->Length;  
---------------------------------------- 
まずは1文字ずつの抜き出しを行いindat配列に入れます。
----------------------------------------
for (a1=0; a1<=nagasa-1 ; a1++){
indat[a1]=cntcomdat->Substring(a1,1);
}
----------------------------------------
次に2文字ごとにペアにします。
----------------------------------------
for (a4=0 ;a4<=comlength-1 ; a4++){
bun[a4]=indat[2*a4]+indat[2*a4+1];
}
----------------------------------------
シリアルにはバイト配列でデータを送るようになっているので変換をします。このときに先の送出用データ5個のデータの並び替えを行います。
----------------------------------------
kako[0]=(char::Parse(bun[4],System::Globalization::NumberStyles::HexNumber));
(以下同じ型式なので省略)
----------------------------------------
後はデータを送ります。comlengthはグローバル宣言でconstで5に初期化しています。送出後にシリアルポートを閉じます。
----------------------------------------
_serialPort1->Write(kako,0,comlength);
_serialPort1->Close();
----------------------------------------
上記の設定でプログラムのUSBコマンドボタンを押すと、無線機のモードがUSBに変わります。ボタン一つしかないと変わったかわからないので別にFMモードもボタン一つ追加してデータ以外はまったく同じコードで動かしています。同じコードでも動くところがCの強みですね。ちなみにコマンドは 
String^ cntcomdat="0C06000000"; // FM 
です。
コマンドで制御ができるようになるといろいろと試したくなると思います。VBでの制御ではいろいろとやりました。VisualC++2010Expressでもシリアル通信の送りと受けがありますので今回の送りは良しとして、受信も試していこうかと思います。まずはいろいろと調べる必要があります。何せC++/CLIは修行の身です。簡単にVBの書き換えでいけば良いのですが。。。。なんか最近は早朝のブログ作成が多くなってきたようだ。もう一眠りしようかしらん!少し眠くなってきた。おわり

vbausbio.dllをVisual C++2008 その7

イメージ 1

イメージ 2

プログラム(多重起動防止)についてプログラミングしてみた。いろいろと方法があるが、2つほど試しにやってみた。1つはMutexのプロセス管理方法、2目つはProcessメッソドを使った方法である。結果から言うと1つ目は、うまく動作していない。現在は保留状態である。なので今は動いているProcessメソッドを用いた方法を採用する。
内容はいたって原理的で、現在のプロセスに登録されている中に、起動しようとしているプロセス名があるかを調べて、そのブール値をもって判断する方法である。
メインは次の一行です。

Process::GetProcessesByName("hamlogw")->Length>0);

この一行を使ったif分分岐をハムログ起動のボタンの最初に書くだけです。画像でコードとメッセージボックス表示状態を載せました。いたって簡単に出来てしまいます。

 1つ目のMutexを使った方法はうまく動作していない。オーソドックスな方法なはずなのだが、どうも次のコード中のif分の判断が出来ていないようだ。
【未動作Mutex】-------------------------------------
using namespace System::Diagnostics;
using namespace System::Threading;

Mutex^ mu = gcnew Mutex(false,"Ham");
if ( mu->WaitOne(0,false) == false )
{
MessageBox::Show("Hamlogw.exeは起動中です。");
return;
}else{
先のハムログ起動コード(省略)
......
mu->ReleaseMutex(); //Mutexの開放
}
----------------------------------------------------
このMutexを使った方法は後で再度やってみたいと思っている。
最近は朝にブログを書くようになってしまった。しかし朝早い時間帯は静かでとても心が休まる。MusicTVで洋楽を流しながらコーディングするのも乙なものだ。

vbausbio.dllをVisual C++2008 その6

イメージ 1

イメージ 2

イメージ 3

夜クーラーをかけていてそのまま寝てしまい朝の1時に目を覚ましてしまった。寝付けないので、確認済みのプログラムをブログアップすることにした。先のVisual C++2010Expressで作成したUSBIOとハムログデータ送受信のVisual C++2008Express版だ。主な違いは先に述べたDLLライブラリのインポート(読み込み)の宣言の違いだけです。他はすべて同じプログラムコードで動きます。この違いをあらためて見てみるとVisual C++2008Expressはすごいと感じています。何がかと言いますと、「ライブラリ名を指定しなくとも目的のライブラリを探して読み込んでくれている。」事です。基本的にはライブラリの存在するフォルダ位置が決まっているから位置はいいとして、ライブラリ数はかなりたくさんあるから、そのなかから定義の関数を探し出していることになります。Visual C++2010Expressはとい言うときちんとライブラリ名を指定しなければなりません。ま私の現状での経験則ですので他の指定しない方法もあるかもしれませんが...上位バージョンだからな~? 指定しない方法わかる方いましたら是非教えてくださいまし。
--------------------------------------------------------------------------------
【FindWindowの例】
☆☆☆ VisualC++2008Express ☆☆☆
extern System::IntPtr __declspec(dllimport) __cdecl FindWindow(unsigned char class Name,unsigned char wndName);

☆☆☆ VisualC++2010Express ☆☆☆
[DllImport("user32.dll")]
extern System::IntPtr FindWindow(String^ className,String^ wndName);
--------------------------------------------------------------------------------
VisualC++2008Expressでも問題なく動きます。マネージドとアンマネージドの混合プログラムはいろいろと大変な変換処理が付きまとう事のほうが少し厄介な気がしています。Frameworkライブラリだけでのプログラムはやりたいことをこのライブラリだけでコーディングするのは実際難しいかも??私の感想です。
時間も3時を回ったので、一応また寝ます。おわり
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード
  • ライブドアブログ