組み込み向けGPS測位情報の処理プログラム C言語版

~NMEAフォーマッのト文字列データ処理と、多品種GPS受信機の制御~

森下功啓製作所 ONLINE

[2013/5/4] 更新したプログラムをアップロードしました。GitHubへアップした方が良いのかな?

1. はじめに

ここでは、GPS受信機では標準的な出力フォーマットとなっているNMEA-0183フォーマット [1] の測位情報を文字列から数値へ変換するプログラムを紹介します。 開発思想的には、「インクルードしたらすぐ使える」を目標としています。 “とりあえずGPSのデータを処理して表示させてみたい”といった場合に便利です。 ここで公開するソースコードはフリーとしますので、どしどし改良されてぜひアップグレードされてください。 私はロボットの制御と環境センサ+GPSロガーやGPSロボットカーに使っています。

現状では、NMEAのGGA,RMC,VTG,GSV, GSAの処理機能を実装しています。 一応、受信機ごとに異なる有効桁の違いや値の省略の仕方にも対応しています(たぶん)。 下記に示すGPS受信機固有の処理についてもいくらか整備しています。 さらに新規の受信機に対応することも簡単です。

表1 GPS受信機の対応状況
GPS受信機名 メーカー 測位情報の処理対応状況 受信機の出力フォーマット 受信機の制御プログラム整備状況
GPS-72,GPS-74シリーズ ポジション 動作確認済 たぶん、NMEA-2.0 整備完了
GH-82シリーズ 古野電気 / FURUNO 動作確認済 古野バイナリ 整備完了
GPS15L / GPS15H ガーミン / GARMIN 動作確認済 NMEA-2.0 整備完了
TK-1315 San Jose Navigation Inc 動作確認済 NMEA-3.0 未整備
ublox5 u-blox 動作未確認 (確か)NMEA-3.0 未整備

2. プログラムの概要

2.1 プログラムの基本動作と開発環境

このプログラムは、C言語を使用して開発された一種のNMEAバーサーです。 NMEAフォーマットの測位情報を処理して、グローバル変数であるGPSR(構造体)へ保存します。 古野バイナリにも対応しており、各種受信機に対して使えるように工夫しています。 保存されたデータには、GPSRの要素へアクセスする事で様々な情報が手に入ります。

普段使っている受信機の関係上、個々の受信機処理部分は最新のバージョンに必ずしも対応していないかもしれませんが、それを発見した方はそっと教えて下さい。

このプログラムの開発には、基本的にはAVRマイコンをターゲットにしているため、AVR studioを使用しています。 また、通常の動作確認はNMEA ver.2を出力するGPS-72D受信機で主に行っていますが、 リリースの際にはNMEA ver.3を出力するTK-1315受信機でも処理が正常に行えるか確認しています。 さらに、GCC Developer Liteを使ってH8マイコンで正常に動作するかの確認も合わせて行っています。

2.2 プログラムパッケージのダウンロード

表2 リリース一覧
リリース日 プログラムパッケージ 更新内容
2013/5/4 GPSreceiveProcessing7.14.5.h 以下の更新を実施しました。
  • ST_NMEA_ENABLEが共用体のつもりでなっていなかったので訂正し、名前をUN_NMEA_ENABLEと改名
  • コメントを見直し
  • ファイルの文字コードをUTF8へ書き換え
  • {}の付け方を最近の自分のスタイルへ合わせた
  • 時刻関係の演算を整理して、time4.0.hへ処理を移した
上記の更新を持って、C言語版の機能追加を終了します。 バグ報告等は大歓迎です。 なお、ビルドがAtmel Studio 6.1で通ることは確認しましたが、最近の受信機でのテストはやっていません。汗)
2013/1/9 - [更新予定]
以下の更新を予定しています。
  • ソースコードを眺めていて発見した軽微なバグを訂正
  • 時刻関係の演算を時刻演算ライブラリへ移動
上記の更新を持って、C版の更新を終了したいと思います。 以降はC++対応版(取りあえず、時刻関係で時刻オブジェクトを使いたい)の開発へ移行します。 また、捕捉衛星数が12を超える受信機が増えてきましたのでその対応のための拡張も実施したいと思います。 さらに、NMEAパーサーをもうちょっとましに作り直したいと思っています(これは更新がいつになるかわからないが)。
2011/11/22 GPSreceiveProcessing7.14.4.h マイナーアップデートです。 デバッグ対策を追加しました。 デバッグモードが有効だと、GGAセンテンスだけ特別なバッファにバックアップします。 詳細はソースコードを参照して下さい。
時刻同期関係は現時点ではまだまだ^4書きかけです。m(-_-)m
2011/1/30 GPSreceiveProcessing7.14.3.h GPSR.ONE_SENTENCE_RECEIVEの扱いに変更を加えました。
また、文字列処理ヘッダを更新しています。
時刻同期関係は現時点ではまだまだ^3書きかけです。m(-_-)m
2011/1/9 GPSreceiveProcessing7.14.1.h ENABLE構造体に共用体を適用して、一度に初期化できるように変更。
Wait_GPS関数において、「GPS受信機からのデータを処理している間は待つ」や「GPSのデータが前回の処理より少なくとも1回は処理」をやめた。
センテンス受信途中での何らかの処理を避けるために、GPSR.ONE_SENTENCE_RECEIVEを追加。

時刻同期関係は現時点ではまだまだ^2書きかけです。m(-_-)m
2010/12/21 GPSreceiveProcessing7.14.h GSAセンテンスの処理及び、MSASを受信しているかどうかを示すSV statusを追加しました。
これにより、測位に使用している衛星がどの衛星かどうかが分かるようになりました。 GSAセンテンスを処理させたい場合、GSAセンテンスを出力するように受信機を設定して、NMEA_ENABLE.GPGSAに1をセットして下さい。 後はマニュアルでの処理や自動処理を行う事で処理されます。

処理結果はGPSR構造体に格納されますので詳しくはコードを読んで下さい。
時刻同期関係は現時点ではまだまだ書きかけです。m(-_-)m
2010/10/10 GPSreceiveProcessing7.13.1.h ver.13の時刻同期に関係する関数で、GPS受信機とマイコン間の通信速度が合わない場合に無限ループに陥るバグが有りましたので若干構造を改めています。 ver.14ではGSAセンテンスの処理を追加したものにする予定です。 調速機能はver.15です。
時刻同期関係は現時点ではまだ書きかけです。m(-_-)m
2010/10/3 GPSreceiveProcessing7.13.h ver.12改をATmega644Pに実装出来ましたので、早速H8マイコンへ移植しようとしたら…実は時刻同期がめんどいと云う事が分かりました。
そこで、時刻同期の方法を関数ポインタを使った方法へ変更しました。
これで真に時刻同期に移植性が出来ましたので、近日中に時刻同期方法について記述します。 少なくとも2週間以内に更新しますので、お楽しみに!
2010/9/20 GPSreceiveProcessing7.12.h
修正・補完版
NMEA ver 2メッセージ、NMEA ver 3メッセージパーサーの両方に対応しました。 その他、細かい修正を加えています。
シリアル出力関数の取り扱い方を統一しています。定義については本体のdefine部分をご覧ください。
時刻同期のための使い方についてはやはりまだ書けていません…
2010/8/24 GPSreceiveProcessing7.12.h [NMEA ver 2メッセージにしか対応できていませんが、先ずは公開という事でベータバージョンです]
GPS-72D受信機でしか動作確認を行えていません。

時刻管理用の関数のバグ修正,センテンスの出力順番自動認識機能を搭載。
今回の更新によって、マイコン内の時計をGPS受信機のシリアル出力に合わせることができるようになりました。 いや、正確に言うと以前からやっていたものに汎用性を持たせたという感じです。 一部の関数の返値の型が変わっています。
時刻同期のための使い方についてはまだ書けていません…
2010/7/17 GPSreceiveProcessing7.11.h 構造体の更新,変数の初期化関数の整備,NMEAフォーマットver3.0への対応など。
TK-1315受信機に関するファイルも追加(今の所はデフォルト処理と変わらない)。
2010/5/5 GPSreceiveProcessing7.7.h GPS受信データの手動処理(フラグ監視モード)に対応。GPS受信機間の通信速度がCPUクロックに対して遅い場合でも対応可能となった。
GPS-72用の制御ヘッダプログラムをマイナーupdate。プログラムメモリ(ROM/RAM)の節約。
2010/4/26
2010/4/27
GPSreceiveProcessing7.6.h うるう秒が14秒と古かったのを修正して、15秒と正しく訂正
time.hに年月日に関する処理を追加
GPS受信機のオリジナルセンテンスを出力させるかどうかを選択機能を追加(今のところ、GPS-72Dのみが対応
動作未確認なのでベータバージョンとします。動作確認版に差し替えましたので正式リリースとします。
2010/4/19 GPSreceiveProcessing7.5.h 衛星情報の出力関数で、衛星数が固定されていたのをdefineで変更できるように修正
2010/4/14 GPSreceiveProcessing7.4.h アルマナック受信状況更新部のいつまでもカウントアップされないバグを修正
GPS関連変数の初期化部で、アルマナックのみを除外するオプションを追加
2010/4/12 GPSreceiveProcessing7.3.h 初公開

*このパッケージはプログラム本体,NMEAフォーマット文字列処理プログラム,GPS受信機固有の処理ヘッダ,文字列処理関数, 数値処理関数,ターゲットマイコン定義,ターゲットGPS受信機の定義ファイルを含みます。


2.3 搭載機能の一覧

現バージョンのプログラムで実現している機能の一覧です。

尚、近い内に整備する予定の機能は以下の3つです。

現時点では受信データのパリティ検査は実施していませんが、今まで問題があったことは無いので大丈夫?だと思います。


2.4 システム要件

C言語で組んでいますので、これをコンパイルできる環境でしたら何でも構わないはずです。
ただし、1秒ごとに割り込みをかけるRTCが必要です。(割り込み周期が1秒でない場合は、RTCの割り込み周期に合わせてtime.h内に宣言された割り込み処理へ内挿する関数を適宜変更する必要があります)


2.5 ハードウェア制約

実装するマイコンにはシリアルポート(ソフトウェア制御でも可)が2つ必要です。 GPS受信機のインターフェイスはシリアル(UART)ですので 通常はシリアルポートが最低2つあり、一方をパソコンなどモニタリングに使用することを前提に開発しました。 シリアルポートが1chしかない場合は、マイコンの受信ポートをGPS受信機の送信ポートへ接続し、マイコンの送信ポートをパソコンなどへ接続する様に割り切ってしまえば使えます。

以下でも説明しますが、処理モードはフラグ監視モード(整備中2010/5/5整備完了!)と割り込みによる自動処理モードがあります。 シリアルの受信割り込みを利用した自動処理モードの場合、対応できる通信速度はAVRのATmegaシリーズでは2400bps/MHzしか能力がありません。 システムクロックは十分に早いものを選んでください。 ATmega644Pではシステムクロック@1.8MHz程度で、4800bpsが安全圏でした。


2.6 シリアル通信処理について

[編集中]

普通は、このソースコードをダウンロードしてコンパイルしてもシリアル関係の関数に関してエラーが出力されるはずです。 シリアル入出力関数を適宜御自分の開発環境に合わせた名前に書き換えて下さい。

本プログラムではシリアル関係の処理は以下の様に宣言されています。 rs_puts()等の名称をお使いのシステムに合わせて修正する必要が有ります。 ベストテクノロジーのGCC Developer Liteとそのサンプルプログラムをお使いならば、そのまま使用できると思います。

シリアル宣言

普通のCの入出力関数ぽくない関数にrs_putsb()が有ります。 これは、バイナリでの出力を意識した関数です。 もし、この様な関数がない場合は以下の様に追加して下さい。

putsb

3. プログラム構造と使い方

GPS受信機から取得した文字列の処理のために、2つの処理モードを用意しています。 割り込みを利用した自動処理モードとフラグ監視モードです。

本プログラムを使用する最小の構成を下に示します。 受信データの処理を担当する大本のプログラムはインクルードしている「GPSreceiveProcessing.h」です。 このプログラムの中でNMEAに関する処理を行っています。 処理結果はGPSRと云う名の構造体に格納しており、簡単にprintf関数で出力させることができます。

3.1 割り込みを使った自動処理バージョン

以下の例では新データを受信した事をフラグで認識していますが、GPS受信機が吐き出したデータそのものの処理は自動で行われるため好きなタイミングで出力しても構いません。 データ処理には時間がかかるため、ある程度高速なCPUクロックが必要です。 通常はシリアルで1文字受信する間に全処理が終了する必要がありますが、受信データフォーマットがNMEAの場合は2,3文字受信出来なかったとしても処理してしまいます。

     1 :	#define  F_CPU        1843200l          // CPU clock
     2 :	#include <micon.h>                      // マイコン独自のヘッダファイルをインクルード。ここでシリアル関係関数のプロトタイプを宣言の事。 
     3 :	#include "GPS.h"                        // GPS受信機定義 
     4 :	#define  GPS_RECEIVER GPS72D_HDR        // 制御対象となる受信機を指定。省略した場合はデフォルト
     5 :	#include "GPSreceiveProcessing.h"       // 受信処理プログラム(本体) 
     6 :	#include "uart.h"                       // シリアル関係のプログラム 
     7 :	    
     8 :	void main(void){
     9 :	
    10 :	    シリアルなどのハードウェア設定;     // 割り込みの処理の許可など 
    11 :	    (void)InitGPSdata(WITH_ALM);        // アルマナック受信状況判断フラグも含めて、GPSの受信データを初期化
    12 :	    NMEA_ENABLE.GPRMC = 1;              // NMEAセンテンスの処理許可ビットを1にすると許可となる 
    13 :	    NMEA_ENABLE.GPGGA = 1;              // この処理は自動/半自動処理のどちらでも必要 
    14 :	    NMEA_ENABLE.GPVTG = 1;
    15 :	    NMEA_ENABLE.GPGSV = 1;
    16 :	    GPSR.AUTO_GPS_PROCESSING = 1;       // GPS受信データの自動処理を許可
    17 :	    GPSR.ACK_OUTPUT_ENABLE = ACK_OUTPUT_ENABLE_FALSE;   // GPS受信機固有のセンテンスを取得した際に、その表示を禁止
    18 :	    while(1){                                           // GPSの処理は自動でやってくれるので、後は出力するだけ 
    19 :	        if(GPSR.GPS_NEW_FLAG){                          // 1セットのセンテンスを処理完了するとtrue
    20 :	            (void)printf("lan:%ld, lon:%ld.\n", GPSR.LAN, GPSR.LON);
    21 :	            GPSR.GPS_NEW_FLAG = 0;
    22 :	        }
    23 :	    }
    24 :	    for(;;);                                            // AVRではこれを付けるとコンパイルサイズが小さくなるらしい 
    25 :	}
		
図3.1.1 シリアル受信割り込みを利用したデータ処理例(2010/9/22 : ver.12用に更新)

3.2 フラグ監視バージョン(逐次データ処理)

こちらは一つのセンテンスのターミネータ(NMEAではLF:0x0a)を受信するごとに立つフラグを監視して、受信データの処理を行う処理方法です。 この処理の特徴は、CPUクロックが比較的低速の場合でも対応可能であることと、割り込みで処理を実行しないので割り込み優先度の低い処理(例えばA/D変換)と共存させやすいという事が挙げられます。 このモードでは一つセンテンスを受信するまでに前の処理が完了していれば良いので、時間的には自動バージョンと比較して20倍以上の余裕があります。 つまり、ATmega644PではCPUクロックが1.8MHz程度の時に4800×20 = 96000bpsでもOKと言う事です。

本プログラムは複数のセンテンス情報を受信した後に処理を実行しても大丈夫なアルゴリズムを採用しました。 バッファサイズが1kbyte以上と余裕がある場合は、1sec経過するごとに処理を実行する事もできます。

     1 :	#define  F_CPU        1843200l          // CPU clock
     2 :	#include <micon.h>                      // マイコン独自のヘッダファイルをインクルード。ここでシリアル関係関数のプロトタイプを宣言の事。 
     3 :	#include "GPS.h"                        // GPS受信機定義 
     4 :	#define  GPS_RECEIVER GPS72D_HDR        // 制御対象となる受信機を指定。省略した場合はデフォルト
     5 :	#include "GPSreceiveProcessing.h"       // 受信処理プログラム(本体) 
     6 :	#include "uart.h"                       // シリアル関係のプログラム 
     7 :	    
     8 :	void main(void){
     9 :	
    10 :	    シリアルなどのハードウェア設定;     // 割り込みの処理の許可など 
    11 :	    (void)InitGPSdata(WITH_ALM);        // アルマナック受信状況判断フラグも含めて、GPSの受信データを初期化
    12 :	    NMEA_ENABLE.GPRMC = 1;              // NMEAセンテンスの処理許可ビットを1にすると許可となる 
    13 :	    NMEA_ENABLE.GPGGA = 1;              // この処理は自動/半自動処理のどちらでも必要 
    14 :	    NMEA_ENABLE.GPVTG = 1;
    15 :	    NMEA_ENABLE.GPGSV = 1;
    16 :	    GPSR.AUTO_GPS_PROCESSING = 0;       // GPS受信データの自動処理を禁止
    17 :	    GPSR.ACK_OUTPUT_ENABLE = ACK_OUTPUT_ENABLE_FALSE;   // GPS受信機固有のセンテンスを取得した際に、その表示を禁止
    18 :	    while(1){                                           // GPSの処理は自動でやってくれるので、後は出力するだけ
    19 :	        if(GPSR.SENTENCE_RECEIVED){                     // センテンスのターミネータを受信を検出したらtrue
    20 :	            (void)GPS_data_processing_manual();         // 受信データの処理を実行
    21 :	            (void)printf("lan:%ld, lon:%ld.\n", GPSR.LAN, GPSR.LON);
    22 :	        }
    23 :	    }
    24 :	    for(;;);                                            // AVRではこれを付けるとコンパイルサイズが小さくなるらしい 
    25 :	}
		
図3.2.1 1センテンスが受信された事を検出してデータ処理を行う例(2010/9/22 : ver.12用に更新)

3.3 NMEA処理後に結果が格納される構造体

GPSの処理データは主に図3.3.3の構造体へ格納されます。 (構造体はそれぞれGPSRとSATELLITE_INFOと云う名で宣言されています。)
例えば、次の様に記述すると緯度[度]が表示されます。
printf("latitude = %ld[deg]\n", GPSR.LAT / 600000l); // printfはシリアル出力関数版

これを見易く "latitude = 32.21235[deg]." と表示するには、次の様にします。

void lat_view(void){
	char lat[11];
				
	(void)Getstr_dd_dd(GPSR.LAT, lat);    // 文字列に変換
	printf("latitude = %s[deg].\n", lat); // 表示する
	return;
}
		
図3.3.1 緯度を表示するコード例

ここで、Getstr_dd_dd(long int position, char *buff)は返値としてbuff…つまり文字列のポインタを返すので上記のコードは次の様にも記述できます。

void lat_view(void){
	char lat[11];
				
	printf("latitude = %s[deg].\n", Getstr_dd_dd(GPSR.LAT, lat)); // 文字列に変換&表示する
	return;
}
		
図3.3.2 緯度を表示するコード例その2

     1 :	//------GPSデータ処理に使用する構造体-----------------------------------
     2 :	
     3 :	typedef struct {                  /*------------GPSから取得したデータやその他から計算した結果を格納しておくための構造体----*/
     4 :	  unsigned long int CLOCK;        // 時刻の数値化したもの。例:“020409”-> [20409](2時4分9秒)
     5 :	  unsigned long int DATE;         // 日付の数値化したもの。例:“020409”-> [90402](2009年4月2日)
     6 :	  long int  LAT;                  // 北緯            ***分単位の10000倍したやつ
     7 :	  long int  LON;                  // 東経
     8 :	  long int  HEIGHT;               // 高度 -9999.9 - 99999.9 小数点以下1桁と仮定して、10倍して保持
     9 :	  double    ASPECT_RATIO;         // 緯度によって変わる局地座標系のLAT方向とLON方向の比を表す。普通は、=cos(lat)
    10 :	  int       DIRECTION;            // 針路(deg単位)
    11 :	  int       SPEED;                // 速度(ノット単位)を10倍したもの
    12 :	  int       HDOP;                 // HDOPを10倍して整数化した数を入れる
    13 :	  int       RECEIVE_COUNTER;      // 受信回数をカウントするのに使用する。
    14 :	  char      SAT;                  // 補足衛星数(数字)を格納
    15 :	  char      GPSON_FLAG;           // GPS受信機からデータを受信するとONになるフラグ。
    16 :	  char      GPSBUSY_FLAG;         // GPS受信データ処理中は1
    17 :	  char      SENTEON_FLAG[KIND_NUM_OF_SENTENCE]; // 受信したセンテンスに対応するフラグを1とする。** NMEAerror の値より大きくなるように気を付けてほしい。
    18 :	  char      GPS_NEW_FLAG;         // 1センテンスの受信が完了するとONになるフラグ
    19 :	  char      SENTE_NUM;            // 受信したセンテンスの番号を入れておく
    20 :	  char      AUTO_GPS_PROCESSING;  // 受信後に自動でセンテンス処理をする場合はこちらを1にする。
    21 :	  char      SENTENCE_RECEIVED;    // 1センテンスの受信がなされると1となる。
    22 :	  char      ASPECT_RATIO_SET_FLAG;// アスペクト比をセットすると1になるフラグ
    23 :	  char      ACK_OUTPUT_ENABLE;    // GPS受信機の応答に関して、許可を行うと出力する。デフォルトでは許可とする。
    24 :	}ST_GPSR2009;//リアルのR
    25 :	
    26 :	typedef struct{                                                     /*-----------------衛星情報------------------*/
    27 :	    int         AZIMUTH;                                            // 方位角(2BYTE)
    28 :	    char        NUMBER;                                             // 衛星番号
    29 :	    char        ELEVATION;                                          // 仰角
    30 :	    char        STATUS;                                             // 受信ステータス SVアキュラシー(精度)
    31 :	    char        LEVEL;                                              // 受信レベル
    32 :	}ST_SATELLITE_INFORMATION;
    33 :	
    34 :	typedef union{
    35 :	    char    BUFF[SAT_INFO_SIZE];
    36 :	    struct{
    37 :	        char                        TOTAL_SATELLITE_NUM;            // 仰角5度以上の視野内衛星数
    38 :	        ST_SATELLITE_INFORMATION    INFORMATION[CORRELATOR_CH_NUM]; // 12衛星分の衛星情報
    39 :	    }ST;
    40 :	}UNI_SATELLITE_INFORMATION;
		
図3.3.3 受信したデータを数値化して格納される構造体


4. 移植の仕方 -HOW TO-

4.1 自動処理バージョンへ対応させるために・・・シリアル受信割り込みへの対応

上で示した自動処理バージョンを利用するには、シリアル受信割り込み処理内において、マクロによって置換される文字列を記述する必要があります。 GPS.h内にマクロを宣言していますので、まずはGPS.hをシリアル受信割り込みに関する記述の前にインクルードしておきます。 次に、シリアル受信処理関数内に
「SCI2_RX_INTERRUPT」
と記述して、sci_rx_dataの部分を受信文字変数名に変えてしまえばOKです。

私の場合は、下記の様に#ifdefを使って、「SCI2_RX_INTERRUPT」が宣言されていた場合に取り込むようにしました。

    71 :	#define    SCI2_RX_INTERRUPT        (void)GPSdata_rx_interrupt(sci_rx_data);
		
図4.1.1 GPSの受信データを割り込みで処理させたい関数を定義している箇所

↓割り込み処理の記述例。自身の割り込み処理部に648行目~650行目をコぴぺするだけです。

   632 :	/****************************
   633 :	    USART1 受信完了割り込み
   634 :	****************************/ 
   635 :	SIGNAL (USART1_RX_vect)
   636 :	{
   637 :	    char c;
   638 :	    volatile short t;    
   639 :	
   640 :	    SCI_1.UCSRA.BYTE &= 0xc7;  // エラーフラグをクリア 
   641 :	    
   642 :	    // 受信 
   643 :	    c = SCI_1.UDR;
   644 :	    t = (FIFO2.rx.wp + 1) % FIFO2.rx.maxlen;
   645 :	    FIFO2.rx.buf[FIFO2.rx.wp] = c;
   646 :	    if (t != FIFO2.rx.rp) FIFO2.rx.wp = t;
   647 :	
   648 :	    #ifdef SCI2_RX_INTERRUPT   // 他のソースから何か処理をしたい場合には、これを宣言しておけば良い。 
   649 :	        SCI2_RX_INTERRUPT
   650 :	    #endif
   651 :	}
		
図4.1.2 割り込み処理の記述例. 

4.2 マイコン内部時刻のGPS時刻への同期と管理

[準備中]

通常、マイコンは測位情報を参照すればUTCに対し1秒以内に同期できますがそれ以上ではありません。 そこで内部時刻をGPS時刻へ数msec以内に同期させ、内部時計のカウントアップを正確に行えるようにします。 ここでは、1PPS信号を使わずにGPS時刻へ同期を取るためにGPS時刻に同期して出力されるシリアル信号を利用する方法を紹介します。 また、1PPS信号を使用した同期は誰が考えても簡単なのでここでは紹介を致しません。
ここで紹介する手法はGPSのシリアル受信データを利用して同期を行います。 従ってGPS受信機の気まぐれな振る舞い(の様に一ユーザー側からは見える)によって上手く行かないことも有るでしょう。 過大な期待はしないようにして下さい。 また、同期を一度取ったとしても普通のマイコンの内部クロックを利用した時計だと2~3秒/日のずれを生じることに留意して下さい。

シリアル受信信号を使って同期を取るためには、GPS受信機の出力が3Dfix時にGPS時刻に同期を取っている必要が有ります。

4.2.1 GPS受信機が出力するセンテンスの出力タイミングについて

3D測位時のGPS受信機が出力するシリアル信号は図4.2.1に示す様に(少なくともTK-1315SUP500F受信機やGPS-72D受信機では)GPS時刻系に同期して出力されています。 マイコンはこのGPS受信機の出力を利用することで10ms以内でGPS時刻へ同期を取ることが出来るのです。 ただし、その出力は良く観察すると図4.2.2の様にオフセットとジッタがあります。 従って、このシリアル信号を利用した同期方法では高々10ms以内の同期精度となります。

シリアル情報の流れと処理の様子
図4.2.1 GPS受信機のシリアル出力とGPS時刻との関係

*センテンスの出力順は受信機によって異なります。
**GSVセンテンスの数は測位状況により増減します。特に相関器の多いubloxなら4ページになることもある。
***センテンスによって手に入る情報が異なります。

Stに含まれる測位情報とは?
位置: Position
時刻: Time
重要なのは上の2つですが、位置についてはディレイ(遅れ)があります。 Position(t) は何と、St-1かSt-2に含まれている事が有ります。 例えば、GPS-15L受信機ではSt-1に含まれていました。

センテンスのジッタとオフセット
図4.2.2 出力センテンスのジッタとオフセット

*シリアル出力は、3D測位時にほぼGPS時刻系に同期して出力される。GMTとは数百msのオフセットがあるので注意。
**ただし、出力にはオフセットがある。オフセット量はシリアル通信速度に依存する。
***ジッタもある。ジッタもシリアル通信速度に依存する。
****センテンスの出力にかかる時間はメッセージ量により変化する。

4.2.2 時刻系について

時刻系には、グリニッジ系(GMT)と世界標準時系(UTC)とGPS時刻系(GPS time)があります1。 この内、GPS時刻系とUTCは1秒の刻みタイミングは一致しています。 また、 GPS時刻系とUTCとでは2010/10現在、GPS時刻系の方が15秒進んでおり、これをうるう秒といいいます。 このオフセットはUTCが地球自転速度の変化に合わせて時々調整されるために生じるものです。 一方で、GMTとUTCの刻みタイミングにはオフセットがあります。 ところで、Windows Vista以前にデスクトップの右下に表示される時計はGMTを使用していて、Windows 7からはUTCを使っているのでこのタイミングの違いは目で確認できます。 ちなみに、2010/10時点での日本標準時はUTCに同期しています。 従って電波時計の刻みとXPマシンの時計の刻みタイミングは一致しません。 GPS受信機のセンテンスに含まれる時刻情報はUTCです。

なお、本プログラムはGPS時刻系へ同期を取ることが出来ます。


1他にも非常に多くの時刻系が定義されています。

4.2.3 時刻同期の下準備

[書きかけにて文章が読めないのはご容赦下さい]

NMEAセンテンスを利用した時刻同期では、シリアル出力の切れ間を見つけることが肝要です。 この切れ間は、最後のセンテンスが出力されてから次のエポックにおけるセンテンスが出力され始めるまでになります。 ところで、NMEAによる測位情報を元にロボットを制御することを考えると、1つのセンテンス受信中での処理を避ける必要が有ります。 また、マイコン側からして所望のセンテンスが出力されていることを確認するためには出力されているセンテンスの認識が欠かせません。 という訳で、その他の事情も絡んで先ずはセンテンスの出力順を認識することとします。

GPS受信機が出力するセンテンスの順番はNMEAでは規定されていないらしく、私の手持ちの受信機では揃っておりません。 そこで、先ずは直接センテンスの出力される間隔を計測し、出力後に最大の間隔が有ったセンテンスが最後のセンテンスで有ったと認識します。 その後に受信されたセンテンスは当然ながら最初に出力されるセンテンスとなります。

4.3 新たに受信機固有の制御プログラム(ヘッダファイル)を追加する場合

デフォルトで対応されている受信機以外のGPS受信機を追加する場合、2つのファイルを修正する必要があります。 一つは、GPS.hのGPS受信機の定義部分です。

    17 :	/***************************************
    18 :	GPS receiver ID
    19 :	GPS受信機固有の制御プログラムが必要とされるので、機種ごとに固有のIDを割り振って、インクルードするファイルを制御する。
    20 :	原理的には市場にある受信機すべての定義は宣言できるはず。
    21 :	****************************************/
    22 :	#define GPS15_HDR                15
    23 :	#define GPS72D_HDR               72
    24 :	#define GHseries_HDR             82
    25 :	#define TK_1315_HDR              1315
    26 :	#define DEFAULT_RECEIVER_HDR     0
		
図4.3.1 GPS受信機の定義 

もう一つは、GPSreceiveProcessing.h内に記述されている、以下の部分です。 既に定義されている部分を真似て追加して下さい。 NMEA解析プログラムの定義は、多くの場合受信機のデータシートにフォーマットのバージョンが記述されていますのでそれに合わせて下さい。

   596 :	//------GPSデータの処理関数群をインクルード(下記ファイルは上記構造体に依存することに注意)---------------------------------
   597 :	// NMEAフォーマットバージョンとGPS受信機には対応問題があるので注意
   598 :	#if     GPS_RECEIVER == GPS72D_HDR
   599 :	    #define    NMEA_PRG_SOURCE        "NMEAv2.0parser_rev7.2.h"    // NMEA処理プログラムの指定
   600 :	    #define    GPS_RECEIVER_HDR       "gps72ver5.4.c"              // ポジション製のレシーバ(GPS72D)用のGPS受信データ処理ヘッダ
   601 :	#elif    GPS_RECEIVER == GHseries_HDR
   602 :	    #define    NMEA_PRG_SOURCE        "NMEAv2.0parser_rev7.2.h"    // NMEA処理プログラムの指定 旧GHシリーズはNMEAフォーマットを出力しないが、特別扱いは疲れるので適当にインクルード
   603 :	    #define    GPS_RECEIVER_HDR       "gh82ver3.1.c"               // 古野製のレシーバ(GH82)用のGPS受信データ処理ヘッダ. 最新バージョンにまだ未対応
   604 :	#elif    GPS_RECEIVER == GPS15_HDR
   605 :	    #define    NMEA_PRG_SOURCE        "NMEAv2.0parser_rev7.2.h"    // NMEA処理プログラムの指定
   606 :	    #define GPS_RECEIVER_HDR          "gps15ver3.3.h"              // GARMIN製のレシーバ(GPS15)用のGPS受信データ処理ヘッダ
   607 :	#elif    GPS_RECEIVER == TK_1315_HDR
   608 :	    #define    NMEA_PRG_SOURCE        "NMEAv3.0parser_rev1.0.h"    // NMEA処理プログラムの指定
   609 :	    #define    GPS_RECEIVER_HDR       "TK1315_ver1.0.c"            // デフォルトの処理ヘッダ
   610 :	#else                                                              // default
   611 :	    #define    NMEA_PRG_SOURCE        "NMEAv2.0parser_rev7.2.h"    // NMEA処理プログラムの指定
   612 :	    #define    GPS_RECEIVER_HDR       "default_GPSreceiver_ver3.3.c"// デフォルトの処理ヘッダ
   613 :	#endif
   614 :	#include    GPS_RECEIVER_HDR
   615 :	#include    NMEA_PRG_SOURCE                                        // NMEAデータの処理関数群をインクルード
		
図4.3.2 指定された受信機用の制御プログラムとNMEA解析プログラムのインクルード箇所


NMEA-0183フォーマットに関する参考文献

リンク
inserted by FC2 system