소년포비의 세계정복!!

스마트폰(PDA) 이용한 차량용 HUD 만들기 #3 - GPS 신호 수신 본문

윈도우폰 세상/Windows Phone

스마트폰(PDA) 이용한 차량용 HUD 만들기 #3 - GPS 신호 수신

소년포비 2009. 10. 29. 23:49

 

GPS 신호를 수신하기 전에 GPS 신호에 대해 간략하게 한번 알아보자.

GPS 에서 제공하는 데이터는 NMEA(The National Marine Electronic Association) code 로 전송되는데, 원래 바다에서 배의 위치를 확인하기 위해 이용되던 녀석이 육지로도 올라와서 차량용 네비게이션등에 활용중이란다.

맞보기로 어떤 녀석들을 보내주는지 데이터를 한번 살펴 보자.

   
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
$GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A 
  


일단 위 GPS 데이터는 Ascen GPS Logger(51CH.) 을 기준으로 나타냈으나, 시중의 다른 GPS 수신기들은 위 데이터외에 다른 데이터가 더 들어갈 수 있음을 미리 알아두자. 그럼 다른 데이터들도 모두 알아야 하느냐~?

그건 아니다.  결론을 먼저 이야기 하자면 위 신호중 우리는 $GPRMC 부분만을 사용할 것이다.
뭔가 잔뜩 들어가 있다.. @_@ 도대체 뭔 소린지는 아래에서 차근차근 살펴보자.
 

모든 신호는 한줄로 나타내고 줄바꿈으로 각각 구분된다.

첫 시작행은 $ 이고 그 이후 줄바꿈 문자가 나타나기 전까지 콤마(,)로 구분된 값들이 나타나는데 각 항목이 특별한 의미를 지니고 있다.

위 신호중 우리가 사용할 $GPRMC 의 각 부분의 의미하는 바는 아래와 같다.


 

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A

앞에서부터 차례대로 적어보면 이렇다.


 

1. 시간 (ZuluTime)
2. 현재 수신 데이터의 유효 여부 : A (Valid), V (Invaild)
     이 데이터가 A 일 경우만 의미가 있다. V 는 아직 위성이 고정되지 않았다는 말.
3. 위도 (Latitude)
4. North,South
5. 경도 (Longitude)
6. East,West
7. 속도(Knots) - 위에서 말한것 처럼 해상에서 배의 위치를 위해 나온것이라 단위가 knots 이다. 킬로미터로 환산하려면 약 1.8 정도를 곱하면 된다.
8. 진행방향(Track Angle) - 자북 방향 기준으로 현재 이동 방향을 나타낸다.
9. 날짜
10. checksum


  

그렇다. 여기서 속도와 진행방향만 parsing 하여 사용하면 된다.

GPS 에 대한 강좌가 아니므로 GPS 신호에 대한 이해는 대강 이정도로만 접어두고, 혹시 더 자세한 정보가 필요하다면 NMEA - http://www.gpsinformation.org/dale/nmea.htm 를 참고 하시라~! 이외의 수많은 sentence 들이 즐비하다.

그럼 이제 준비된 GPS 모듈로 부터 NMEA 데이터를 받아보자. 외부 GPS 수신기와 단말기(또는 테스트PC)가 블루투스로 연결되면 우리는 단순히 serial port 를 하나 열어 들어오는 NMEA  데이터를 족족~ 읽어들이기만 하면 된다.

Serial Port 를 열기 위해 System.IO.Ports.SerialPort  객체를 생성하고 아래와 같은 코드를 작성한다.

블루투스로 바인딩된 포트는 COM1 이라 가정하고 해당 Port 에 들어오는 NMEA  데이터를 가져오는 코드는 아래와 같다.

* Read Serial Port
  

this.serialPort.PortName = "COM1";
this.serialPort.Open();
string msg = this.serialPort.ReadExisting(); 

미리 생성된 Serial Port 객체에 바인딩할 COM1 이름만 할당하고 ReadExisting 으로 읽어들이면 되는데, 매초 단위로 읽어야 하므로 위 코드는 Timer 객체의 onTick Event Handler 로 등록해주면 된다.

이외에 해당 Port 의 내용을 가져올때 원활한 처리를 위한 delegate 몇개를 생성했다.


* Event Delegator
  

public delegate void on_FIND_PORT(object sender, int port);
public event on_FIND_PORT onFindPort;
public delegate void on_FOUND_PORT(object sender, int port);
public event on_FOUND_PORT onFoundPort;
public delegate void on_READ_PORT(object sender, string message);
public event on_READ_PORT onReadPort;
public delegate void on_PARSE_GPRMC(object sender, GPSInfo info);
public event on_PARSE_GPRMC onParseGPRMC; 


  

이제 GPS 모듈로 부터 NMEA 데이터를 가져오기 위한 대강의 준비는 완료되었다.

그럼 이제 NMEA 데이터를 파싱하는 code 를 살펴보자

* NMEA Data Parser
 


            public GPSInfo Parse(string msg)
            {
                if (msg == null || msg.Length <= 0)
                {
                    throw new Exception("There are no data in port");
                }
                GPSInfo info = new GPSInfo();
                info.Speed      = 0;
                info.Latitude   = 0;
                info.Longitude  = 0;

                string[] lines = msg.Split('$');

                IEnumerator ie = lines.GetEnumerator();


                while (ie.MoveNext())
                {
                    string[] items = ((string)ie.Current).Split(',');

                    if (items[0].Equals("GPRMC"))
                    {
                        if (items[GPRMC_VALID].Equals("A"))
                        {


                            info.Speed             = Double.Parse(items[GPRMC_KNOT]) * KNOT_TO_KMS;
                            info.Latitude          = Double.Parse(items[GPRMC_LATITUDE]);
                            info.LatitudeType  = items[GPRMC_LATITUDE_TYPE];
                            info.Longitude        = Double.Parse(items[GPRMC_LONGITUDE]);
                            info.LongitudeType = items[GPRMC_LONGITUDE_TYPE];
                            info.Angle                  = Double.Parse(items[GPRMC_ANGLE]);


                            if (this.OnParseGPRMC != null)
                            {
                                onParseGPRMC(this, info);
                            }
                        }

                        return info;
                    }
                }

                return info;

            }
 


  
정리하자면 라인단위로 split 하고 다시 , 단위로 split 한 뒤 각 요소위치별로 데이터를 추출하는 것인데, 이때 속도 부분만 1.83 을 곱해 knots 를 km/h 로 변환하였다.
 
가져온 값들은 각각 현재 이동 속도(Speed), 위도(Latitude), 경도(Longitude), 진행 방향(Angle)이다.
 
이제 이 값들을 적절히 화면에 출력하는 일만 남았다.