○ GPSモジュール

アマゾンなどで低価格で売っているGPSモジュールを使用します。
アマゾンでのこの手のGPSモジュール
このモジュールはマイコンでの工作例はよく見かけるのですが、いまいち使い方がよくわからない。
そこで、直接パソコンに接続してGPSのデータを見てみたいと思います。

今回使用するGY-GPS6MV2モジュールはこちらになります。


次にGPSモジュールとパソコンと接続するためのCH340モジュールを用意します。
アマゾンでのCH340モジュール

この二つのモジュールのTXとRXを交互に接続し、VCCを5V、GNDとGNDを接続しました。
もし、5V対応していないモジュールの場合には電圧を変換する必要があると思います。


実際に接続した所です。

このモジュールのUSBをパソコンに接続します。
接続後にデバイスマネージャで接続先ポートを確認したところ、COM3に接続されていました。
COM3に対して9600bpsで接続するとGPSのデータが見れるはずです。
どのようなクライアントソフトを使っても良いですが、私は自作ソフトを使いポートを開きました。
自作ソフトのRS232c テスト用アプリケーション

自作ソフトでCOM3を開くと空の受信データが定期的に表示されました。
モジュールを窓際に置いてしばらく放置すると受信できたようで、時間や位置情報が表示されるようになりました。
この文字列をNMEAフォーマットと言うらしく、簡単な文字列操作でデータが取り出せそうです。


■ パソコンの時間を設定するアプリを作成する

GPSからの文字列を操作して時間を取得して、パソコンの時間を設定するアプリが簡単に作れるんじゃないかと思い作成することにしました。
まずは、時間を取得して設定する方法から。
Windows10だと、アプリ自体に管理者権限が無いとパソコンの時間の設定が出来ないようで、管理者権限で実行をする必要があります。
あと、パソコン自体の日付の自動調整を切っておく必要があります。
まずは、パソコンの日付をインクリメントするコード
#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
	SYSTEMTIME st;

	//現在の協定世界時 UTC を取得します
	GetSystemTime(&st);
	printf("%d/%d/%d %d:%d:%d.%d\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
	st.wDay++;
	//時間の設定
	SetSystemTime(&st); 

	GetSystemTime(&st);
	printf("%d/%d/%d %d:%d:%d.%d\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
	getchar();
    return 0;
}
時間を取得して日付けに1を足して時間を設定、その後再取得して表示しています。
そのため、月末ではうまく動作しません。
協定世界時なので日本時間だと9時間足す必要があります。


▽ 管理者権限で実行したくない場合

SE_SYSTEMTIME_NAME特権をアプリに与えるとうまくいくらしいのですが私のWindows10ではダメでした。
#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
	HANDLE TokenHandle;
	LUID Luid;
	TOKEN_PRIVILEGES NewState;

	if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&TokenHandle)){
		return -1;
	}

	if(!LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &Luid)){
		return -1;
	}

	NewState.PrivilegeCount = 1;
	NewState.Privileges[0].Luid = Luid;
	NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	AdjustTokenPrivileges(TokenHandle,FALSE,&NewState, 0, (PTOKEN_PRIVILEGES)NULL, 0);
	if(GetLastError() != ERROR_SUCCESS){
		return -1;
	}

	SYSTEMTIME st;

	//現在の協定世界時 UTC を取得します
	GetSystemTime(&st);
	printf("%d/%d/%d %d:%d:%d.%d\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
	st.wDay++;
	//時間の設定
	SetSystemTime(&st); 

	GetSystemTime(&st);
	printf("%d/%d/%d %d:%d:%d.%d\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
	getchar();
    return 0;
}

このコードでは特権が取得できず動作しませんでした。
そこで、ローカルセキュリティポリシーを変更する事で解決しました。
ローカルセキュリティポリシーを変更すると特権を取得も出来ましたが、そもそも特権自体が必要ないみたいです。
つまり、最初のコードでパソコンの時間をサクサク変更できました。


コントロールパネル → 管理ツール → ローカルセキュリティポリシーの順に開き、システム時間の変更を選択します。


ココにUsersを追加すると動くのですが一筋縄ではいきません、ユーザーまたはグループの追加を押します。


まずは、 Users と入力しOKボタンを押します。


そうすると、見つかりませんと言ってきますのでオブジェクトの種類ボタンを押します。


グループにチェックを入れてOKボタンを押します。


Usersが無事追加されました、適用ボタンを押してから、パソコン自体を再起動すると時間を変更し放題になります。



▼ RS232c通信ソフトのベースを作成

いきなり何もない所から作成するのは無理なので、ベースとなるソフトを作成しました。

接続ボタンを押すとポートに接続されて、文字列を受信し表示します。
Visual C++ 6.0で作成しました。
RS232cで接続して文字を受信するソフト rs232cBase1.zip


▼ NMEAフォーマットのチェックサム

データに誤り検出符号が入っているようなので検出したいと思います。
具体的には1行毎の末尾の * の後ろに入っており、 先頭 $ と * の間を計算

$?????????????*##

上記の形であるデータのの部分を一文字毎にXORを取って16進数の形式を取る##部分の値と一致すればデータは正常という事らしいです。
この仕様に従ってプログラムを作成しました。
#include <stdio.h>
#include <stdlib.h>

char*str="$GPGSA,A,3,04,06,17,19,03,09,14,,,,,,2.79,1.38,2.42*03\n";

//チェックサムを計算 0なら正常 それ以外は異常
int checkSum(char*data){
	char c=0,str[3];
	while(*++data!='*'){
		c=c ^ *data;
	}
	str[0]=*++data;str[1]=*++data;str[2]=NULL;
	char i=(char)strtol(str,NULL,16);
	if(i==c){
		return 0;
	}else{
		return -1;
	}
}

int main(int argc, char *argv[])
{
	int i=checkSum(str);
	printf("%d\n",i);
	getchar();
	return 0;
}
作成したのはチェックサムを確認する関数です。
受信データを流し込んで、正常なら処理、異常なら破棄、という処理に使います。


▼ 文字列の切り出し方法

NMEAフォーマットのデータはカンマで区切ったデータで出来てきます。
そこで、 区切り文字による文字列の切り出し の strtok により切り出そうと思います。
試した所、 strtok は連続した区切り文字をまとめてしまうためダメでした。
そのため、自作関数を作成して対応しました。


▼ NMEAのデータ構造

データを見てみると、行の種類が複数あるようです。
必要そうなデータを含んだGGAの時刻や位置とGPS関連の情報をまとめた行を利用しようと思います。

$GPGGA,1,2,N,4,E,6,7,8,9,M,11,M,13,14*15

1 時刻(UTC)日本標準時は+9
2 緯度
4 緯度
6 GPSの品質 受信不能:0 標準測位:1 干渉測位:2
7 使用衛星数
8 精度劣化の垂直成分
9 アンテナの海抜高さ
11 ジオイド高さ
13 補正情報の経過時間
14 補正情報基準局のID
15 チェックサム

ここではGPSの品質が良ければ、時間が正確なんだろうと判断してプログラムを作成していきたいと思います。


■ GPSによりパソコンの時間を調整するソフトの作成

これで必要なネタは全てそろったはずですので後は組み立てるだけです。
1分毎に調整するのか、1時間ごとに調整するのか、1日毎に調整するのか、を決めてプログラムするだけです。
最初に大きな道筋を考えて、一つ一つ積み上げて最後に組み立てるとこんなに簡単に目的の物を作る事が出来ます。
最初から最終形態のソフトを作成しながら足りない技術をかきあつめて作成しようとすると、設計がぐちゃぐちゃになり断念するハメになりやすいです。
大切なのは細かく刻んで進むということじゃないかな?と私は思います。
あと、可能な限りシンプルに、どちらにしても最後はぐちゃぐちゃになりますから。


ということで、GPSでの時間を調整するソフトを作成してみました。
GPSの秒が30秒になった瞬間にパソコンの秒と比較して違うなら30.500秒に設定します。
1秒毎の秒数を見ているので+1秒の誤差、GPSのミリ秒部分は見ていないので-999msの誤差、+500msで設定するので+500msの誤差
合計で+1秒〜-1.5秒の誤差が出ると思います。
今回作成したソフト gps_rs232c_1.zip
私のGPSではミリ秒の値が入ってからしばらくすると送信タイミングが合わせられてミリ秒の値が0になりますので+1秒〜-0.5秒の誤差になると思います
もっと誤差を減らすには、GPSから時間を受信した瞬間にパソコンの時間を設定すると良いと思います。
RS232c.hに問題があるためCOM10以降には接続できません。


▼ソフトの小さな改造


このままでは使いにくいので最小化ボタンと、起動時に自動接続チェックボックスを作成しました。
RS232c.hを修正しました、COM10以降にも接続できます。
今回作成したソフト gps_rs232c_2.zip


▲トップページ > マイコンなど