소속 : 배재대학교 전자공학과 Digital System Lab.
만든이 : 03학번 김충겸
GPS 의 이해
현재 랩실에는 USB 타입의 GPS 가 있다.
전원공급+데이터입/출력을 USB 한개로만 한다.
GPS 데이터의 형식은 NMEA 08 로서 아래와 같다.
자세한 형식은 아래 파일을 다운로드 하시기 바랍니다.
GPS.pdf
----------------------------------------------------------------------------------------
GPS의 메시지 구성은 이렇습니다.
$GP<메시지ID: 3문자><데이터: 콤마로 구분>*<checksum: 16진수 2문자>CR/LF
GPS메시지는 한문장에 모든 정보를 다 주는것이 아니라
특정 메시지ID에는 특정 정보가 있는 것입니다.
즉 여러번 메시지를 받아서 원하는 정보를 골라 내는것입니다.
또한 에러체크를 해서 신뢰할수 없는 데이터를 제외시켜야하죠
그리고 또 GPS메시지를 몇개의 위성으로부터 받았나 그것도 잘봐야합니다.
0개면 제대로되지 않은 메시지죠 즉 건물안에 있거나 하면 0이 뜹니다.
------------------------------------------------
/* 에러검사
* checksum : '*' 뒤의 16진수 두자리로 표시한다.
* '$'와 '*' 사이의 각 문자를 XOR한 값을 표시한다.
*/
GPS메시지의 * 다음의 16진수 두문자는 체크썸 값입니다.
메시지의 $다음부터 *전까지의 문자들을 XOR 연산하여 나온값이 체크썸 값입니다.
일치하지 않으면 에러인것이죠
---------------------------------------------------
데이터 부분은 콤마로 구분되어서 여러가지 데이터가 있습니다.
주의할점은 메시지마다 데이터 갯수가 다 같지 않습니다.
어떤것은 9개 어떤것은 12개 이렇게 콤마로 구분되어 있습니다.
<GPS 메시지의 예>
$GPRMC,120757,A,5152.985,N,00205.733,W,000.0,349.4,230100,004.1,W*78\r\n
자바를 이용한 GPS 메시지 파싱
Java를 이용하여 시리얼 통신을 하려면
Java는 고급 언어입니다. 그래서 저수준 제어를 할수가 없습니다.누군가가 이 점을 보완하려고 JNI(Java Native Interface)를 이용하여
Java에 시리얼 통신을 할수 있도록 만든 패키지가 있습니다.
그것이 바로 Java Communications API 라는 것입니다.
현재는 Sun 사의 지원아래 개발되어 있고 버전은 3.0 까지 나와있습니다.
공식 개발된 버전은 솔라리스와 리눅스만 있습니다. 윈도우에서 사용하시려면 구버전인 2.0을 사용하셔야 합니다.
아래 파일을 다운받으시기 바랍니다.
commapi_2_0.zip
설치법 :
- win32com.dll 파일을 <java 설치경로>/bin/ 에 복사합니다.
- comm.jar 파일을 Java에서 import 하여 사용하시면 됩니다.
공식 사이트 : http://java.sun.com/products/javacomm/
API : http://java.sun.com/products/javacomm/reference/api/index.html
API serial features:
- Enumeration of ports (administrator and user configurable port mapping)
- Port configuration (baud rate, speed, stop bits, parity)
- Access to EIA232 standard DTR, CD, CTS, RTS and DSR signals
- Transfer of data over RS-232 ports
- Hardware and software flow-control options
- Receive-buffer threshold control
- Asynchronous event option for notification of:
- Data available on an RS-232 port
- Port hardware line level changes
- Port ownership changes within a single JVM
GPS 메시지 저장 객체
다운로드 : Message.java[codejava]
/*
GPS 메시지를 저장하는 객체
소속 : 배재대학교 전자공학과 Digital System Lab.
만든이 : 03학번 김충겸
*/
package dsl.sna.gps;
import java.util.StringTokenizer;
public class Message {
String id = null;
String[] data = null;
public Message(){
}
public Message(String id, String[] data){
setID(id);
setData(data);
}
public void setID(String id){
this.id = id;
}
public void setData(String[] data){
this.data = data;
}
public String getID(){
return this.id;
}
public String[] getData(){
return this.data;
}
public String getData(int idx){
return this.data[idx];
}
}
/*
GPS 메시지를 저장하는 객체
소속 : 배재대학교 전자공학과 Digital System Lab.
만든이 : 03학번 김충겸
*/
package dsl.sna.gps;
import java.util.StringTokenizer;
public class Message {
String id = null;
String[] data = null;
public Message(){
}
public Message(String id, String[] data){
setID(id);
setData(data);
}
public void setID(String id){
this.id = id;
}
public void setData(String[] data){
this.data = data;
}
public String getID(){
return this.id;
}
public String[] getData(){
return this.data;
}
public String getData(int idx){
return this.data[idx];
}
}
GPS 메시지를 받고 파싱 객체 (이걸로 실행하시면됩니다.)
다운로드 : GPS.java
[codejava]
/*
GPS 메시지를 시리얼통신을 이용하여 받고
NMEA08 형식의 메시지를 파싱하여 출력한다.
소속 : 배재대학교 전자공학과 Digital System Lab.
만든이 : 03학번 김충겸
*/
package dsl.sna.gps;
import javax.comm.CommPortIdentifier;
import javax.comm.NoSuchPortException;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.StringTokenizer;
import dsl.sna.usart.*;
public class GPS implements SerialPortEventListener {
Usart gps = null;
String message = "";
boolean is_msg_start = false;
public static void main(String[] args) {
new GPS();
}
public GPS(){
this.gps = new Usart("COM4", 9600);
this.gps.addEventListener(this);
}
// 시리얼포트에서 메시지가 수신되면 호출된다.
public void serialEvent(SerialPortEvent e) {
if(e.getEventType() == SerialPortEvent.DATA_AVAILABLE){
String read = this.gps.read();
/*
* 메시지는 1문자씩 날라온다
* 시작문자 : '$'
* 끝 문자 : CR/LF
*/
if(read.equals("$")){ // $ : 메시지를 받기 시작해도 된다.
this.is_msg_start = true;
}
if(this.is_msg_start){ // 메시지 받기가 시작되었으면 차곡차곡 저장한다.
this.message += read;
}
if(read.equals("\n")){ // CR/LF : 메시지 한문장이 종료 되었다.
this.message = this.message.trim(); // 앞뒤 빈공백 제거 \r이 여러개 붙은경우도 있어서;;
if(this.message.length()>0){
this.notifyMessage(this.message); // 수신된 메시지를 처리한다.
}
this.is_msg_start = false;
this.message = "";
}
}
}
// 한문장이 수신되었을때 호출된다.
public void notifyMessage(String recv){
/* 에러검사
* checksum : '*' 뒤의 16진수 두자리로 표시한다.
* '$'와 '*' 사이의 각 문자를 XOR한 값을 표시한다.
*/
// 메시지에서 checksum 추출
int idx = recv.indexOf("*");
String checksum = recv.substring(idx+1, idx+3);
// 메시지를 각 문자를 XOR한 결과값을 구한다.
int xor = 0;
for(int i=1; i<idx; i++){
xor ^= recv.charAt(i);
}
// checksum과 xor한 결과를 비교 한다.
if(checksum.equals(Integer.toHexString(xor).toUpperCase())){ // 사용할수 있는 데이터
Message msg = parseMessage(recv);
if(msg.getID().equals("GGA")){
// 2 1 Latitude 경도
String latitude = msg.getData(2)+" "+msg.getData(1);
// 4 3 Longitude 위도
String longitude = msg.getData(4)+" "+msg.getData(3);
// 5 Position Fix 위치의 결정이 되었는지
//if(msg.getData(5).equals("0")){ // 값이 0이면 신뢰할수 없는 데이터
// return;
//}
// 6 Satellites Used 현재 수신되는 위성 갯수
String satellite_num = msg.getData(6);
// 8 9 Altitude 평균 해수면을 기준으로 한 고도
String altitude = msg.getData(8)+msg.getData(9);
System.out.println("경도: "+latitude+" 위도: "+longitude+" 위성수: "+satellite_num+" 고도: "+altitude);
} else if(msg.getID().equals("RMC")){
Calendar time = Calendar.getInstance(Locale.KOREA);
// 0 UTC Time 세계 표준시
time.set(Calendar.HOUR, Integer.parseInt(msg.getData(0).substring(0, 2)));
time.set(Calendar.MINUTE, Integer.parseInt(msg.getData(0).substring(2,4)));
time.set(Calendar.SECOND, Integer.parseInt(msg.getData(0).substring(4,6)));
time.add(Calendar.HOUR, 9); // 한국시간은 UTC시간에 9시간을 더한다. (날짜말고 시간만;;)
// 8 UTC Date
time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(msg.getData(8).substring(0,2)));
time.set(Calendar.MONTH, Integer.parseInt(msg.getData(8).substring(2,4)));
time.set(Calendar.YEAR, Integer.parseInt("20"+msg.getData(8).substring(4,6)));
System.out.println("["+time.get(Calendar.YEAR)+"/"+time.get(Calendar.MONTH)+"/"+time.get(Calendar.DAY_OF_MONTH)+" "+time.get(Calendar.HOUR_OF_DAY)+":"+time.get(Calendar.MINUTE)+":"+time.get(Calendar.SECOND)+"]");
// 1 Status 신뢰할수 있는 위치 정보인가
//if(msg.getData(1).equals("V")){ // 신뢰할 수 없다.
// return;
//}
// 6 Speed over ground 해양용 속도 Knots 단위
// Knots를 Km/hr로 변환하려면 1.852를 곱해야한다.
String speed = (Float.parseFloat(msg.getData(6))*1.852)+" Km/hr";
// 7 Course over ground 진북을 중심으로한 진행방향
String direction = "NE"+msg.getData(7);
System.out.println("진행방향: "+direction+" 속도: "+speed);
} else if(msg.getID().equals("GSA")){
// 1 Mode (1: no fix, 2: 2d, 3: 3d)
String mode = null;
switch(Integer.parseInt(msg.getData
(1))){
case 1:
mode = "No fix";
break;
case 2:
mode = "2D";
break;
case 3:
mode = "3D";
break;
}
}
}
}
// 메시지를 포맷대로 추출한다.
public Message parseMessage(String recv){
/*
* 메시지 포맷
* $GP<메시지ID: 3문자><데이터: 콤마로 구분>*<checksum: 16진수 2문자>CR/LF
*
*/
// 메시지 ID (3문자)
String id = recv.substring(3,6);
// 데이터
String[] data = recv.substring(7, recv.indexOf("*")-1).split(",");
return new Message(id, data);
}
}
/*
GPS 메시지를 시리얼통신을 이용하여 받고
NMEA08 형식의 메시지를 파싱하여 출력한다.
소속 : 배재대학교 전자공학과 Digital System Lab.
만든이 : 03학번 김충겸
*/
package dsl.sna.gps;
import javax.comm.CommPortIdentifier;
import javax.comm.NoSuchPortException;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.StringTokenizer;
import dsl.sna.usart.*;
public class GPS implements SerialPortEventListener {
Usart gps = null;
String message = "";
boolean is_msg_start = false;
public static void main(String[] args) {
new GPS();
}
public GPS(){
this.gps = new Usart("COM4", 9600);
this.gps.addEventListener(this);
}
// 시리얼포트에서 메시지가 수신되면 호출된다.
public void serialEvent(SerialPortEvent e) {
if(e.getEventType() == SerialPortEvent.DATA_AVAILABLE){
String read = this.gps.read();
/*
* 메시지는 1문자씩 날라온다
* 시작문자 : '$'
* 끝 문자 : CR/LF
*/
if(read.equals("$")){ // $ : 메시지를 받기 시작해도 된다.
this.is_msg_start = true;
}
if(this.is_msg_start){ // 메시지 받기가 시작되었으면 차곡차곡 저장한다.
this.message += read;
}
if(read.equals("\n")){ // CR/LF : 메시지 한문장이 종료 되었다.
this.message = this.message.trim(); // 앞뒤 빈공백 제거 \r이 여러개 붙은경우도 있어서;;
if(this.message.length()>0){
this.notifyMessage(this.message); // 수신된 메시지를 처리한다.
}
this.is_msg_start = false;
this.message = "";
}
}
}
// 한문장이 수신되었을때 호출된다.
public void notifyMessage(String recv){
/* 에러검사
* checksum : '*' 뒤의 16진수 두자리로 표시한다.
* '$'와 '*' 사이의 각 문자를 XOR한 값을 표시한다.
*/
// 메시지에서 checksum 추출
int idx = recv.indexOf("*");
String checksum = recv.substring(idx+1, idx+3);
// 메시지를 각 문자를 XOR한 결과값을 구한다.
int xor = 0;
for(int i=1; i<idx; i++){
xor ^= recv.charAt(i);
}
// checksum과 xor한 결과를 비교 한다.
if(checksum.equals(Integer.toHexString(xor).toUpperCase())){ // 사용할수 있는 데이터
Message msg = parseMessage(recv);
if(msg.getID().equals("GGA")){
// 2 1 Latitude 경도
String latitude = msg.getData(2)+" "+msg.getData(1);
// 4 3 Longitude 위도
String longitude = msg.getData(4)+" "+msg.getData(3);
// 5 Position Fix 위치의 결정이 되었는지
//if(msg.getData(5).equals("0")){ // 값이 0이면 신뢰할수 없는 데이터
// return;
//}
// 6 Satellites Used 현재 수신되는 위성 갯수
String satellite_num = msg.getData(6);
// 8 9 Altitude 평균 해수면을 기준으로 한 고도
String altitude = msg.getData(8)+msg.getData(9);
System.out.println("경도: "+latitude+" 위도: "+longitude+" 위성수: "+satellite_num+" 고도: "+altitude);
} else if(msg.getID().equals("RMC")){
Calendar time = Calendar.getInstance(Locale.KOREA);
// 0 UTC Time 세계 표준시
time.set(Calendar.HOUR, Integer.parseInt(msg.getData(0).substring(0, 2)));
time.set(Calendar.MINUTE, Integer.parseInt(msg.getData(0).substring(2,4)));
time.set(Calendar.SECOND, Integer.parseInt(msg.getData(0).substring(4,6)));
time.add(Calendar.HOUR, 9); // 한국시간은 UTC시간에 9시간을 더한다. (날짜말고 시간만;;)
// 8 UTC Date
time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(msg.getData(8).substring(0,2)));
time.set(Calendar.MONTH, Integer.parseInt(msg.getData(8).substring(2,4)));
time.set(Calendar.YEAR, Integer.parseInt("20"+msg.getData(8).substring(4,6)));
System.out.println("["+time.get(Calendar.YEAR)+"/"+time.get(Calendar.MONTH)+"/"+time.get(Calendar.DAY_OF_MONTH)+" "+time.get(Calendar.HOUR_OF_DAY)+":"+time.get(Calendar.MINUTE)+":"+time.get(Calendar.SECOND)+"]");
// 1 Status 신뢰할수 있는 위치 정보인가
//if(msg.getData(1).equals("V")){ // 신뢰할 수 없다.
// return;
//}
// 6 Speed over ground 해양용 속도 Knots 단위
// Knots를 Km/hr로 변환하려면 1.852를 곱해야한다.
String speed = (Float.parseFloat(msg.getData(6))*1.852)+" Km/hr";
// 7 Course over ground 진북을 중심으로한 진행방향
String direction = "NE"+msg.getData(7);
System.out.println("진행방향: "+direction+" 속도: "+speed);
} else if(msg.getID().equals("GSA")){
// 1 Mode (1: no fix, 2: 2d, 3: 3d)
String mode = null;
switch(Integer.parseInt(msg.getData
(1))){
case 1:
mode = "No fix";
break;
case 2:
mode = "2D";
break;
case 3:
mode = "3D";
break;
}
}
}
}
// 메시지를 포맷대로 추출한다.
public Message parseMessage(String recv){
/*
* 메시지 포맷
* $GP<메시지ID: 3문자><데이터: 콤마로 구분>*<checksum: 16진수 2문자>CR/LF
*
*/
// 메시지 ID (3문자)
String id = recv.substring(3,6);
// 데이터
String[] data = recv.substring(7, recv.indexOf("*")-1).split(",");
return new Message(id, data);
}
}
'dev, tech > navigation' 카테고리의 다른 글
GPS GPS 정의 GPS 특징 GPS의 구성요소 (0) | 2008.09.01 |
---|---|
최단거리 알고리즘 (0) | 2008.09.01 |
스마트폰(PDA) 이용한 차량용 HUD 만들기 #3 - GPS 신호 수신 (0) | 2008.09.01 |
gps 파싱 (0) | 2008.09.01 |
GPS 응용프로그램 작성 (0) | 2008.09.01 |
댓글