제임스딘딘의
Tech & Life

개발자의 기록 노트/GPS 이야기

[NMEA-0183] Checksum 계산 예제코드 (How to calculate checksum of NMEA-0183)

제임스-딘딘 2017. 8. 26. 15:52

본 포스팅에서 다뤄볼 내용은 NMEA-0183 문장의 checksum 계산 방법이다.

보통 GPS chip은 해석한 GPS신호를 NMEA라는 특정 형태의 프로토콜로 제공한다.

NMEA 가 무엇인지에 대해서 더 자세히 알고싶다면 아래 포스팅을 참고하도록 하자.


2017/08/10 - [하드웨어 Note/GPS 이야기] - [GPS 이야기] NMEA-0183 형식, NMEA Sentence Format


위 포스팅에 나와있듯이, NMEA sentence는 $ 문자로 시작해서 * 문자로 끝난다.

checksum의 계산은 $, * 의 사이에 있는 각각의 문자 하나하나를 exclusive-or (XOR) 연산 및 누적하면 된다.

어떤 NMEA sentence가 주어졌을 때, "*" 뒤에 있는 16진수 두자리의 checksum도 같이 주어질 텐데, 직접 XOR연산하여 checksum을 계산한 값과 주어진 checksum값이 일치하는지를 검사해야 한다. 일치하지 않는다면, 그 sentence는 통신상의 문제 등의 이유로, 결함이 있는 것으로 간주해야 하며, 사용하면 안된다. 




구체적인 계산 예제 코드를 제시한다.




NMEA 체크섬 계산 함수(JAVA 버전)

char checkSum(String nmeaStr) {
  char check = 0;
  // iterate over the string, XOR each byte with the total sum:
  for (int c = 0; c < theseChars.length(); c++) {
    check = char(check ^ theseChars.charAt(c));
  } 
  // return the result
  return check;
}


인자 'nmeaStr' 은 $와 * 사이의 NMEA문자열을 넣으면 된다. 각 문자 하나하나를 XOR연산하여 'check' 변수에 저장하여 반환한다.

return type은 char 이다. 이것을 2bytes hex string으로 변환해서 사용하면 된다.




NMEA 체크섬 계산 함수 (C 버전 1)

#include <stdint.h>

#define NMEA_END_CHAR_1 '\n'
#define NMEA_MAX_LENGTH 70

uint8_t
nmea_get_checksum(const char *nmea_str)
{
    const char *n = NULL;
    uint8_t chk = 0;
    if (strcmp(nmea_str[0], '$') == 0) {
        n = nmea_str + 1; // skip '$' by plus one
    } else {
        n = nmea_str;
    }

    /* While current char isn't '*' or sentence ending (newline) */
    while ('*' != *n && NMEA_END_CHAR_1 != *n) {
        if ('\0' == *n || n - sentence > NMEA_MAX_LENGTH) {
            /* Sentence too long or short */
            return 0;
        }
        chk ^= (uint8_t) *n;
        n++;
    }

    return chk;
}


인자 nmea_str은 $와 * 사이의 NMEA sentence를 넣으면 된다. 그러나 만약 인자로 들어오는 NMEA sentence가 $ 를 포함하고 있다면 생략하도록 방어코드를 넣어 두었다.

이제, checksum 값은 아래와 같이 계산하면 된다.

GPRMCBuf 가 NMEA sentence이다.


uint8_t chk = nmea_get_checksum(GPRMCBuf);



NMEA 체크섬 계산 함수 (C 버전 2)

또다른 방법으로 NMEA의 checksum 계산을 구현한 C언어 버전이다. 

첫번째 인자 'buf'가 계산하려는 NMEA sentence이다. 위의 버전1과는 달리 $ 부터 * 까지의 NMEA sentence를 인자로 넣어도 된다. 

두번째 인자 'len'는 buf의 길이이다.

int calc_NMEA_Checksum( char *buf, int len )
{
    char Character;
    int Checksum = 0;
    int i;              // loop counter

    // foreach(char Character in sentence)
    for (i=0; i < len; ++i)
    {
        Character = buf[i];
        switch(Character)
        {
            case '$':
                // Ignore the dollar sign
                break;
            case '*':
                // Stop processing before the asterisk
                i = len;
                continue;
            default:
                // Is this the first value for the checksum?
                if (Checksum == 0)
                {
                    // Yes. Set the checksum to the value
                    Checksum = Character;
                }
                else
                {
                    // No. XOR the checksum with this character's value
                    Checksum = Checksum ^ Character;
                }
                break;
        }
    }

    // Return the checksum
    return Checksum;
}



이 함수를 통해 checksum을 계산하면 int 형 결과가 반환된다.

이제, 비교하려는 NMEA sentence에서 2bytes의 char형 checksum 문자를 읽고, 1byte의 hex형으로 변환 한 후, 위 함수의 반환값과 비교하면 된다.