지난 호에서는 GPS에 대한 전반적인 설명, NMEA 프로토콜, 시리얼 통신 등 실제 GPS 수신 프로그램을 구현하기 위한 기본적인 내용을 다뤘다. 이번 호에서는 프로그램 소스를 보면서 NMEA-0183 프로토콜이 어떻게 사용되는지 상세 설명과 수신된 데이터를 파싱(parsing)하는 방법을 중심으로 살펴볼 것이다. 시중에는 다양한 GPS 수신기가 존재하지만 어떤 수신기라도 모두 COM 포트를 통해 데이터를 전송한다. 그러므로 자신이 사용하는 단말기가 GPS 수신기와 몇 번 포트로 연결되는지, 전송 속도는 얼마로 설정하는지 등 포트 오픈 시에 필요한 정보를 미리 알아둬야 한다. GPS 수신 프로그램을 모두 구현하고도 데이터가 수신되지 않는다면 대부분 포트 설정 문제일 것이다.
데이터 유효성 검사
수신된 데이터를 파싱하기 앞서 수신된 데이터 값이 올바른 데이터인지 아닌지를 검사하는 방법과 쉽게 문장의 필드를 구분하는 방법을 알아보자.
$GPRMC,120757,A,5152.985,N,00205.733,W,000.0,349.4,230100,004.1,W*78<CR><LF>
각각의 NMEA 문장은 체크썸(Checksum)을 포함하고 있다. ‘*’ 이후에 나오는 16진수 두 자리 숫자가 바로 체크썸이며 ‘$’와 ‘*’ 사이의 값을 모두 XOR한 값이다. 앞의 예의 경우는 78이 체크썸이며 문장을 수신한 후에는 체크썸을 계산한 후 ‘*’ 뒤에 표시된 값과 비교해 문장의 유효성을 검증해야 한다. 체크썸이 맞지 않다면 당연히 이 데이터는 사용하면 안된다.
$GPGSA,A,3,,05,06,,09,,,24,,29,30,,3.9,2.5,2.9*30<CR><LF>
앞의 예제를 $, 콤마(,), *, <CR>, <LF>를 구분자로 strtok을 하면 어떻게 될까? strtok 함수를 호출할 때마다 GPGSA, A, 3, 05, 06, 09, 24 등 이러한 순서대로 스트링이 분할되어 반환된다. strtok을 사용하면 무척이나 편리하지만 여기에는 한 가지 문제가 존재한다. ‘,,,’와 같이 구분자가 연속해서 있는 경우에는 이 사실을 알 방법이 없다. NMEA 프로토콜은 각 필드의 내용 구분을 필드 값의 순서로 하기에 순서가 밀리면 안 된다. 예제의 경우 strtok을 사용했을 때의 결과 값을 사용한다면 1채널이 수신하고 있는 위성의 ID는 05가 되고 2채널의 위성 ID는 06, 3채널의 위성 ID는 09가 된
다. 이런 식으로 값을 분석하면 PDOP, HDOP, VDOP 값을 알 도리가 없다.
실제로 NMEA 문장을 받아 로그를 찍어보면 예제와 같이 콤마가 연속하여 나오는 경우가 자주 있다. 그러므로 구분자가 연속해서 나오는 경우에는 해당 필드 값이 비어있다는 것을 알 수 있어야 한다. 올바른 파싱 결과는 <표 1>과 같아야 한다.
올바른 파싱 결과를 위해 CDataParse::GetToken, GetWToken 함수에서는 콤마가 연속해 있는 경우에는 필드 값이 없어 ‘NONE’을 리턴하도록 코드를 작성해야 한다. 비단 이것만이 아니라 실제 NMEA 수신에 있어서 예외 상황이 발생할 수 있다. 이런 예외 상황에 대한 예외 처리는 순전히 프로그램 개발자의 몫이다.
데이터 파싱하기
이제 문장 별로 필드 값을 구별해 화면에 보여줄 차례다. 실질적인 파싱은 CMainSheet::GPSDataProc 함수에서 처리한다. PDA는 유니코드(Unicode)를 사용하기에 일단 체크썸 검증이 완료된 문장은 MultiByteToWideChar를 사용해 WCHAR로 변환한 후 사용하도록 한다. 한 함수에서 파싱과 화면에 표시하는 작업을 모두 하고 있기에 다소 코드가 길다. 다음에 나올 코드상의 ‘: Skip’ 주석이 달린 부분은 필드 값을 가져오나 화면상에 표시하지 않는 값을 가리킨다.CMain sheet::GPSDataProc 함수의 중요 코드를 분석해 나가면서 각 Sentence ID에 따른 필드 값에 대해서도 상세하게 설명하겠다.
간단히 시리얼 통신이라 생각하면 되고 GPS 데이터만 수신하면 되기에 쓰레드에 들어오는 값을 NMEA 프로토콜을 이용해 파싱만 하면 된다. 여기서는 수신한 데이터를 파싱하는 부분에 대해 핵심만 설명하겠다. 나머지 프로그램 소스는 유저 인터페이스 부분이므로 특별히 여기서는 다루지 않고 ‘이달의 디스켓’을 참고하기 바란다.
이제부터 본격적으로 GPS 신호 수신 프로그램을 살펴볼 것이고, 첫 번째로 XUINT CMainSheet::GPSDataProc(char *pBuf, int nLen) 함수의 내부 코드를 살펴보자. GPSDataProc(char *pBuf, int nLen) 함수는 데이터의 유효성 검증, 유니코드 변환, Sentence ID 구분과 Sentence ID에 따른 필드 화면 표시의 기능을 모두 구현하기에 코드가 길다. 그러므로 이 함수를 각 기능 별 코드로 분리해 살펴보자.
※ 자료출저 : www.imaso.co.kr