○ GPSモジュール

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

今回使用するNEO-6シリーズのモジュールはこちらになります。


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

このGPSモジュールは電源以外は5Vに対応していません。
そこで、5Vで動作させる場合には、RXを接続しないほうが良いと思います。

Arduinoなどで5V環境で扱う場合にはオレンジのライン (モジュールの受信側) は接続しないほうがよいです。
安全に接続する必要がある場合には、ロジック レベル 変換回路を使用するのが正しいです。
GPSのデータを読むだけなら、そもそもRXは接続する必要がないからです。

CH340モジュールでは3.3V動作に変更できるようになっていました。
そこで、CH340モジュールをVCCと3V3をショートして3.3Vで動作するようにしました。
もちろんGPSモジュールの電源も3V3端子から取っています。


別のタイプのGPSモジュールですがRX TXを接続しました、これならGPSモジュールにコマンドを送れますのでモジュールの設定変更ができます。


GPSモジュールと接続したCH340モジュールをパソコンに接続します。
接続後にデバイスマネージャで接続先ポートを確認したところ、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秒に設定します。
GPSの送信タイミングとデータを見てみるとミリ秒部分は0で送信しているようです、パソコンの時刻の設定は+500msで設定するので常に+500msの誤差が発生し、
パソコンの誤差は最大で マイナス側はコンマ何秒 〜 +1秒 の範囲だと思います。
今回作成したソフト gps_rs232c_1.zip
もっと誤差を減らすには、GPSから時間を受信した瞬間にミリ秒を含めてパソコンの時間を設定すると良いと思います。
RS232c.hに問題があるためCOM10以降には接続できません。


▼ソフトの小さな改造


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


▼もっと高精度にする試み

前回作成したソフトではパソコンの時刻の誤差が500ms近く出てしまいますので、もっと高性能なソフトを作成したいと思います。
そこで、毎分ごとに30秒になったら時間・分・秒・ミリ秒を問答無用で毎回設定する事にしました。
幸運な事に、時間の設定で使用している SetSystemTime は、Windows2000でもWindows10でもUTCの時間を設定する仕様みたいなので、そのまま設定しています。
GPSから取得した2桁のミリ秒も10倍して3桁にして設定しています。
今回作成したソフト GPSで時間を合わせる_Ver_0_1


▼使いやすくする試み

ソフトが起動中、ウインドウを表示しておくのもよくないので、タスクバーに格納されるようにしました。
今回作成したソフト "GPSで時間を合わせる Ver 1.2" 実行ファイルを含む

実行ファイルをネットからダウンロードし実行すると最近ではWindows Defenderがすべてウイルスと判断し登録してしまい、 その後は同様のファイルは全てウイルスと判断されるようです。
心配な方は絶対に確実なソースコードから実行ファイルを作成するのをおすすめします。
ソースコードのみ


■ GPSモジュールの設定の仕方

今回使っているGPSモジュールは U-blox という会社が作っている NEO-6 シリーズのモジュールです。
GY-NEO-6MV2と書いてあるのを買ったので、NEO-6 シリーズの水晶発振バージョンでしょうか。

U-blox のサイトから、u-center というソフトをダウンロードするとモジュール自体の設定ができるようです。
私は、こちらからu-centerをダウンロードしました。
古いモジュールのため、u-center2ではなくてu-centerが必要です。

まずは、ダウンロードしてインストールします。
その後、ソフトを開いてGPSモジュールと接続します。
Receiver → Connection → 接続先ポート を選択して接続します。


▼ 送信レートの設定

デフォルトでは1秒毎にデータが送信されますが、これを2秒毎に変更してみます。
View → Configuration View を開いて設定画面を出します。


設定画面でRATEを探してクリックし、2000ms に変更して Send ボタンを押します。

これにより2秒毎にデータが送信されるように変更されました。
データのタイミングがゆっくりになっているのが確認できると思います。

しかし、保存されていないため電源を落とすと初期状態に戻ります。
この状態をモジュールに記憶させるには、
Receiver → Action → Save Config を選択することによりモジュール自体に書き込まれ、電源を落としても戻りません。



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