要素を取り出して表示する方法について質問です – プログラミング – Home

要素を取り出して表示する方法について質...
 
通知
すべてクリア

要素を取り出して表示する方法について質問です


mio
 mio
(@mio)
ゲスト
結合: 16年前
投稿: 3
Topic starter  

はじめましてmioと申します。
私はLINUXでC言語を使用して,GPS端末から受信したデータをシリアル通信でPCに表示す
るプログラムを作成しています。
GPS端末から受信するデータはNMEAというフォーマットのデータで,緯度経度や時刻に
関する情報を文字と数値で表しています。

最初の段階として,受信したデータをそのまま表示する機能は実現出来たのですが,
次の段階として,表示したい3つの緯度・経度・時刻のみを取り出して表示する機能を付
加しようと思い,NMEAデータから緯度経度時刻のすべてを含む行を判別して必要な部分を
取り出すために,strtok関数とstrcmp関数を使用して、緯度経度時刻の表示も可能になり
ました。
しかしここで,上記2つの機能をあわせて,指定した回数判別して表示する機能を実現した
いのですが, セグメンテーション違反です と表示されてしまうのを解決することが出
来ず困っています。
どの点に問題があるのかご指摘をお願いいたします。
以下は上記にあげた二つのプログラムになります。

①指定した回数NMEAデータを受信して表示するプログラム
#include<unistd.h>
#include<fcntl.h>
#include<strings.h>
#include<termios.h>
#include<stdio.h>

#define BAUDRATE B4800
#define MODEMDEVICE /dev/ttyS0

int main()
{
int fd, c, res;
int i=0;
int j=0;
struct termios oldtio, newtio;
unsigned char buf[512];

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);

while (1) {
res = read(fd,buf,512);
buf[--res]=0; j++;
printf(%s\n,buf);
j++;
if(j==12)break; /*NMEAセンテンス12行表示*/
}
tcsetattr(fd, TCSANOW, &oldtio);
close(fd);
return(0);
}

実行結果
$GPRMC,081312,A,3428.9433,N,13549.4905,E,0.0,0.0,060808,6.7,W,A*06

$GPRMB,A,,,,,,,,,,,,A,A*

$GPGSA,A,3,03,06,07,,16,,21,,25,27,,,2.3,2.0,1.0*34
・・と12行このようなNMEAセンテンスを表示します。


引用解決済
トピックタグ
mio
 mio
(@mio)
ゲスト
結合: 16年前
投稿: 3
Topic starter  

②NMEAセンテンスから時刻、緯度、経度のみを出力
二つ目の機能として、必要な部分のみを取り出す機能を、NMEAのうちの時刻緯度経度の
すべてを含んだセンテンスであるGPRMCを模した配列に対して試します。

#include <stdio.h>
#include <string.h>
#include <math.h>

char buf[512]
=$GPRMC,143514,A,3428.9468,N,13549.4507,E,0.0,196.2,180708,6.7,W,A*05; /*緯
度経度時刻がすべて含まれているGPRMCからデータを取得したいのでGPRMCのモデルを1行
用意*/

void pnmea(void) {
int utc;
double lat; /*緯*/
double lon; /*経*/
char ew; /*東西*/
char ns; /*南北*/
char stat;
char *p;
/*一行のNMEAセンテンスから時刻緯度経度のみを取り出すために、strtokで”,”ごとの
トークンを得る*/
if ((p=strtok(buf,,))==NULL) return;
if (strcmp(p,$GPRMC)!=0) return; /*センテンスの先頭は$GPRMCか?*/
if ((p=strtok(NULL,,))==NULL) return;
sscanf(p,%d,&utc);
if ((p=strtok(NULL,,))==NULL) return;
stat=p[0];
if ((p=strtok(NULL,,))==NULL) return; /緯度*/
sscanf(p,%lf,&lat);
if ((p=strtok(NULL,,))==NULL) return;
ns=p[0];
if ((p=strtok(NULL,,))==NULL) return; /*経度*/
sscanf(p,%lf,&lon);
if ((p=strtok(NULL,,))==NULL) return;
ew=p[0];

if (stat!='A') return;
printf(%02d時%02d分%02d秒(UTC) ,utc/10000, (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));
if (ew=='E') printf(東経); else if (ns=='W') printf(西経);
printf(%.0lf度%.4lf分,floor(lon/100.0), fmod(lon,100.0));
printf(\n);
}
int main(void) {
pnmea();
return 0;
}
②実行結果
14時35分14秒(UTC) 北緯34度28.9468分東経135度49.4507分


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

res = read(fd,buf,512);
buf[--res]=0; j++;

ここんとこで res > 0 であることは保証されていますか?
そうでないとbuf[-1]をアクセスしちゃいますが。


返信引用
mio
 mio
(@mio)
ゲスト
結合: 16年前
投稿: 3
Topic starter  

①と②を組み合わせたプログラム③として、
#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);
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);
if (strcmp(p,$GPRMC)!=0) return; /*センテンスの先頭は$GPRMCか?*/
if ((p=strtok(NULL,,))==NULL); /*utc:世界標準時*/
sscanf(p,%d,&utc);
if ((p=strtok(NULL,,))==NULL); /*stat:ステータス*/
stat=p[0];
if ((p=strtok(NULL,,))==NULL); /*lat:緯度*/
sscanf(p,%lf,&lat);
if ((p=strtok(NULL,,))==NULL); /*ns:南北*/
ns=p[0];
if ((p=strtok(NULL,,))==NULL); /*lon:経度*/
sscanf(p,%lf,&lon);
if ((p=strtok(NULL,,))==NULL); /*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);
}
}
/*時刻緯度経度に対応するトークン表示*/
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);
}

プログラム②のreturn文はすべて消去して、上記のように組み合わせました。
ここで実行を行うと一行目にはその時刻に対応して
16時34分14秒(UTC) 北緯34度28.9468分東経135度49.4507分と②の機能通り表示してくれ
るのですが、その次の行ではセグメンテーション違反です と表示され、そこで結果は
終了してしまいます。
配列の容量に問題があるのでしょうか?
乱文で申し訳ありませんが、ご指摘よろしくお願いいたします。


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

えーと、どうやって動作確認をしているのでしょう?
gdbを使っているのですか?
それともprintfでコンソール上でしょうか?

上記の説明で「その次の行ではセグメンテーション違反です」とあるのは
次の行が実行されたときの話ですか?
それともコンソール上の次の行の話でしょうか?

もしコンソール上の次の行の話をしているのであれば、
それだけではまだまだ追求が足りないと思います。
処理単位毎にでもprintfを入れて見て何処まで動いてそれが出ているのかを
ちゃんと追いかけないと場所の特定が出来ないと思います。

ソースを見ても見当がつかないのであれば、
必要であれば、printfをバンバン入れてまずは場所の特定が
出来るように調べて見た方が良いと思いますよ。


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

ソースを貼る時はインデントが保持されるようにして
貼らないと多分わかりにくいです。
スペースかなんかを入れてインデントがわかるようにしてください。
特に長いソースはとても見づらくなります。

ぱっと見変だと思った事。

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

この部分がWhileループの中にあるのに

  fclose(fp_gps);

これは外にある。

これだと毎回毎回、シリアルポートを開きに行きませんか?
しかも、前回のオープンの後始末(fclose)をしていないのに。

しかも毎回、tcgetattrしているから最初に取得したパラメータは喪失している。

readでしくじった時の処理が全く入っていない。

くらいかな。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました