본문 바로가기
아두이노

아두이노로 GPS 신호를 직접 처리(분석) 하기

by 구루가 되고픈 2019. 3. 10.

아두이노로 GPS 신호를 직접 처리(분석) 하기

 

현재 작업중이 차량 대시보드에 붙이려고 GPS수신모듈을 구매하여 진행방향과 고도 정보를 표시하려고 작업을 진행하였습니다.

 

GPS수신기는 UBLOX 제품으로 NEO-6M GPS 모듈 GY-GPS6MV2 입니다.

 

저럼한 가격이 장점인 NEO-6M GPS수신기

 

이 제품은 아두이노에서 일반적으로 많이 사용하는 수신기로 알리로는 4~5천원대에 구매가 가능한 가성비 GPS수신기 입니다.

 

GPS를 통해 얻을 수 있는 정보는 매우 다양한데, 그중 차량용 대시보드에 사용하려는 정보는 차량 진행방향과(0~359도 방위각) 고도값(미터) 2가지만 뽑아내려고 합니다.

 

가장 쉬운 방법은 tinyGPS 같은 라이브러리를 사용하는 방법입니다.

 

#include <SoftwareSerial.h>
#include <TinyGPS.h>

TinyGPS gps;
SoftwareSerial nss(11, 12);

void setup()
{
  Serial.begin(9600);
  nss.begin(9600);
}

void loop()
{
  while (nss.available())
  {
    int c = nss.read();
    if (gps.encode(c))
    {
      float falt = gps.f_altitude(); // +/- altitude in meters
      float fc = gps.f_course(); // course in degrees
      float fkmph = gps.f_speed_kmph(); // speed in km/hr

      Serial.println(falt);
      Serial.println(fc);
      Serial.println(fkmph);
      Serial.println("");
    }
  }
}

 

이 코드를 실행시켜보면 GPS 신호가 1초 간격으로 수신되면서 시리얼 모니터에 표시됩니다.

 

그렇지만 이 글에서는 GPS신호를 직접 파싱(처리)해서 원하는 정보를 가져오는 방법으로 해 보겠습니다.

그러기 위해서는 일단 GPS신호의 구조부터 알아야 합니다.

 

GPS신호는 간단한 구조의 문자열로 되어 있습니다

GPS신호는 GPGGA, GPGSV, GPRMC로 시작하는 문자열로 문자열 형태로 수신되고, 문자열은 경도, 위도 같은 값들이 콤마(,)로 구분되어 있습니다.

 

아래가 GPS신호 문자열 예제인데요,

GPGGA 문자열의 빨간색 숫자(46.9)는 고도를 나타냅니다.

GPRMC 파란색 숫자(22.4)는 속도를 나타내는데 속도는 노트(kont)단위 입니다.

다음 빨간색 숫자(84.4)는 진행하고 있는 방향의 방위각(0~359)을 나타냅니다.

 

$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

 

나머지 숫자의 의미는 아래 블로그에 굉장히 상세히 설명되어 있으니 관심 있으신 분들은 방문하시면 도움이 되실겁니다.

 

http://erine.egloos.com/2233135

 

GPS문자열을 파싱해서 원하는 값을 구해보도록 하겠습니다.

일단 신호자체가 문자열인데다가, 각 값은 정해진 위치에 콤마(,)로 구분되어 있기 때문에 손쉽게 추출이 가능합니다.

아래 예제는 고도, 속도, 방위값 3개의 정보를 추출하는 코드입니다.

 

String gpsstr = "";
String targetStr1 = "GPGGA";
String targetStr2 = "GPRMC";
String salt, sdir, sknot;
float alt, dir, knot, spd;
uint8_t s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;

void setup(void)
{
  Serial.begin(9600);
}

void loop(void)
{
  if(Serial.available())
  {
    gpsstr = Serial.readStringUntil('\n');

    // GPGGA
    if(targetStr1.equals(gpsstr.substring(1, 6)))
    {
      s1 = gpsstr.indexOf(",");
      s2 = gpsstr.indexOf(",", s1+1);
      s3 = gpsstr.indexOf(",", s2+1);
      s4 = gpsstr.indexOf(",", s3+1);
      s5 = gpsstr.indexOf(",", s4+1);
      s6 = gpsstr.indexOf(",", s5+1);
      s6 = gpsstr.indexOf(",", s6+1);
      s8 = gpsstr.indexOf(",", s7+1);
      s9 = gpsstr.indexOf(",", s8+1);
      s10 = gpsstr.indexOf(",", s9+1);
      salt = gpsstr.substring(s9+1, s10);
      alt = salt.toFloat();
    }

    // GPRMC
    if(targetStr2.equals(gpsstr.substring(1, 6)))
    {
      s1 = gpsstr.indexOf(",");
      s2 = gpsstr.indexOf(",", s1+1);
      s3 = gpsstr.indexOf(",", s2+1);
      s4 = gpsstr.indexOf(",", s3+1);
      s5 = gpsstr.indexOf(",", s4+1);
      s6 = gpsstr.indexOf(",", s5+1);
      s7 = gpsstr.indexOf(",", s6+1);
      s8 = gpsstr.indexOf(",", s7+1);
      s9 = gpsstr.indexOf(",", s8+1);

      sknot = gpsstr.substring(s7+1, s8);
      sdir = gpsstr.substring(s8+1, s9);
      knot = sknot.toFloat();
      spd = knot * 1.852;
      dir = sdir.toFloat();
    }
      
    gpsstr = "";
  }
}

 

gps 수신 문자열을 한줄씩 읽어서 gpsstr에 저장합니다.

 

그후 앞 6개 글자를 비교해서 GPGGA로 시작하는 문자열인 경우 콤마(,)의 위치를 indexOf()으로 계산해서 고도값을 추출한 후 float형으로 변환하여 저장합니다.

(추출한 값 자체가 문자열이기 때문에 처리를 위해 변환이 필요)

 

이후 마찬가지로 GPRMC의 문자열인 경우도 동일한 방법으로 속도값과 고도값을 추출하여 각각 float형으로 변환하여 저장합니다.

 

다만 knot를 km로 저장하기 위해 1.852를 곱해 주었습니다.

 

코드의 핵심은 문자열에서 콤마의 위치를 알아내서 해당 문자열을 추출하는 것입니다.

그외에는 그다지 어려운 부분은 없습니다.

 

이 코드를 조금만 추가하면 위도, 경도도 알아낼 수 있고,  gps가 제공하는 다양한 정보중에 필요한 정보를 충분히 추출해 낼 수 있으니 응용해 보시면 될것 같습니다.