プロが教えるわが家の防犯対策術!

現在C言語を使用して、受信したデータから必要な部分だけを表示するプログラムを作成しております。
以前作成した、GPSからデータを受信するpg1と、必要な部分のみ表示するpg2をあわせることで、受信したデータから必要な部分を抜き出すことができました。
次の段階として、そのプログラムを指定した回数表示が行えるよう変更を試みたのですが、一度表示するとそこで終了してしまう状態から抜け出せません。どの点を改良すれば、回数を指定して表示ができるようになるのでしょうか。よろしくお願いいたします。

このときGPS端末はRS232CでPCに接続GPS端末で受信しています。
受信したNMEAと呼ばれるフォーマットのデータから時刻,緯度,経度に対応する要素を取り出して表示しようとしています。
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<termios.h>
#include<stdio.h>
#include<math.h>

#define BAUDRATE B4800
#define MODEMDEVICE "/dev/ttyS0"

int main()
{
int fd, c, res;
int utc;
char stat;
char ns;
char ew;
char *p;
double lat;
double lon;
struct termios oldtio, newtio;
char buf[512];
FILE *fp_gps;

while(1){
if((fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ))==-1){
perror(MODEMDEVICE);
exit(-1);
}

tcgetattr(fd, &oldtio); /*シリアルポートの設定を待避*/
bzero(&newtio, sizeof(newtio)); /*新しいポートの設定の構造体をクリア*/
newtio.c_cflag = (BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD);/*ボー:4800*/
newtio.c_iflag = (IGNPAR | ICRNL);
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

res = read(fd,buf,512);
buf[--res]=0; /* 文字列終端をセット */


if ((p=strtok(buf,","))==NULL) return;
if (strcmp(p,"$GPRMC")!=0) return; /*センテンスの先頭は$GPRMCか?*/
if ((p=strtok(NULL,","))==NULL) return; /*utc:世界標準時*/
sscanf(p,"%d",&utc);
if ((p=strtok(NULL,","))==NULL) return; /*stat:ステータス*/
stat=p[0];
if ((p=strtok(NULL,","))==NULL) return; /*lat:緯度*/
sscanf(p,"%lf",&lat);
if ((p=strtok(NULL,","))==NULL) return; /*ns:南北*/
ns=p[0];
if ((p=strtok(NULL,","))==NULL) return; /*lon:経度*/
sscanf(p,"%lf",&lon);
if ((p=strtok(NULL,","))==NULL) return; /*ew:東西*/
ew=p[0];

if (ew==p[0]){
fp_gps = fopen("gdata/gdata.dat","w");
if (fp_gps == NULL){
printf("open fp_gps err\n");
}
}
/*時刻緯度経度に対応するトークン表示*/
if (stat!='A') return; /*ステータスチェック*/
printf("%02d時%02d分%02d秒(UTC) ",utc/10000+9, (utc%10000)/100, utc%100);/*utc+9時間は日本時間における標準時*/
fprintf(fp_gps,"%02d時%02d分%02d秒(UTC) ",utc/10000+9, (utc%10000)/100, utc%100);

if (ns=='N') printf("北緯"); else if (ns=='S') printf("南緯");
printf("%.0lf度%.4lf分",floor(lat/100.0), fmod(lat,100.0));
fprintf(fp_gps,"%.0lf度%.4lf分",floor(lat/100.0), fmod(lat,100.0));

if (ew=='E') printf("東経"); else if (ns=='W') printf("西経");
printf("%.0lf度%.4lf分",floor(lon/100.0), fmod(lon,100.0));
fprintf(fp_gps,"%.0lf度%.4lf分",floor(lon/100.0), fmod(lon,100.0));
printf("\n");

}


tcsetattr(fd, TCSANOW, &oldtio); /*退避前の設定に戻す*/
fclose(fp_gps);
close(fd); /*ポートを閉じる */
}
/*実行結果
./a.out
./a.out
14時15分48秒(UTC) 北緯34度28.9393分東経136度49.4900分
*/実行結果は上記のように出力され、何も表示されないこともあります。ご指摘よろしくお願いいたします。

A 回答 (2件)

>2行目に入ってGPRMC以外の文と判断されてしまうことで



この部分がよくわからないのですが、read関数を抜けると、
bufには$で始まるNMEAの1行が取れているんですね?

以下の部分はwhile文の外で1度だけ行えば良いと思うのですが、
なぜ毎回オープンするようにしているのでしょうか。

if((fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ))==-1){

tcsetattr(fd,TCSANOW,&newtio);

while文は、
while(1) {
 read();
 解釈
}
でよくて、解釈部分の
if (strcmp(p,"$GPRMC")!=0) return; /*センテンスの先頭は$GPRMCか?*/
がreturnとしてしまっているので、プログラムが終了してしまっているのでしょう。
このreturnをcontinueにしましょう。
while文の先頭に戻って、次の行をreadしにいくはずです。
    • good
    • 0

read直後のbufの内容をfprintfで書き出して、


文字列解析処理に入る前に、どういう文字列が入っているのかを
チェックしたほうがいいと思うよ。
何回目のreadで読めた情報、というのも合わせて書くようにして。

文字列解析処理が想定していない文字列が渡ってきてreturnして
しまっているんだと思うんですが。

この回答への補足

>hidebunさん回答ありがとうございます。
受信しているデータは$GPRMC,093514,A,3428.9468,N,13649.4907,E,0.0,196.2,180708,6.7,W,A*05

$GPRMB,A,,,,,,,,,,,,A,A*0B

$GPGGA,093514,3428.9468,N,13649.4907,E,1,07,2.1,13.2,M,35.9,M,,*7E

$GPGSA,A,3,03,06,07,,16,,21,,25,27,,,2.3,2.1,1.0*35

$GPGSV,3,1,11,03,76,315,47,06,72,017,51,07,21,318,47,13,24,282,00*7D
・・・・・
・・・・・
・・・・・と続くNMEAデータで、
hidebunさんのおっしゃるように
$GPRMC以外の文が先頭に来たり、2行目に入ってGPRMC以外の文と判断されてしまうことで、returnしてしまっているようです。

ここで扱っているNMEAデータは、$GPRMCや$GPRMBなど位置情報など12種類の情報で1セットとなっており、12種類目を受信した後にその時点での情報に更新された先頭の$GPRMCを再び受信してゆきます。
他の11種の文を無視して、この$GPRMCから指定した回数の時刻,緯度,経度を取り出して表示するには、文字列解析処理部分からreturnを除いて何らかの処理を加えればよいでしょうか?

補足日時:2008/09/11 15:29
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!