### 0.0.1 체크섬(Checksum) 무결성 검증을 위한 XOR 연산 로직 구현체

### 0.0.1 체크섬(Checksum) 무결성 검증을 위한 XOR 연산 로직 구현체

가혹한 전자기 간섭(EMI)과 진동이 수반되는 멀티로터의 하드웨어 배선 사이로, 수백 kbps에 달하는 직렬(Serial/UART) 데이터 스트림이 흐를 때 비트 플립(Bit-flip)이나 바이트 유실(Byte Drop) 현상은 필연적으로 발생한다.

만약 위치 추정기(EKF2)가 “위도 37도“를 “위도 87도“로 잘못 파싱한 패킷을 단 한 프레임이라도 삼키게 된다면, 기체는 순간적으로 수천 킬로미터를 이동해야 한다고 판단하여 모터를 최대 출력으로 오작동시키는 이른바 플라이어웨이(Flyaway) 사고를 일으킬 것이다. 이러한 비극을 시스템의 최전선에서 차단하는 최후의 보루가 바로 NMEA 체크섬(Checksum) 검증 로직이다.

0.1 NMEA 0183 체크섬의 수학적 정의

NMEA 프레임 구조의 꼬리에는 메시지 스트림의 무결성을 증명하는 8비트 길이의 해시(Hash) 서명이 항상 따라붙는다. 이 해시는 복잡한 다항식 나눗셈을 요구하는 CRC(Cyclic Redundancy Check)가 아니라, 프로세서 관점에서 가장 가벼운 명령어인 배타적 논리합(XOR, Exclusive OR) 누적 구조로 정의된다.

  • 설계 규칙: 체크섬은 달러 기호($) 바로 다음 문자부터 시작하여, 체크섬 시작을 알리는 별표 기호(*) 직전까지 존재하는 모든 문자의 ASCII 코드 값을 순차적으로 XOR 연산한 결과값이다.
  • 표기법: XOR 연산 결과로 도출된 8비트 숫자(0~255)는 두 자리의 16진수 문자열(예: 4A, B2)로 변환되어 별표(*) 바로 뒤에 문자열 형태로 덧붙여진다.

0.2 PX4 파서 엔진 내부의 XOR 누산기(Accumulator) 구현

PX4의 NMEA 파싱 코드를 관장하는 src/drivers/gps/nmea.cpp를 살펴보면, 바이트가 하나씩 들어올 때마다 상태 머신(State Machine) 루프 내에서 체크섬을 점진적으로 누산(Accumulate)하는 매우 가볍고 우아한 로직을 발견할 수 있다.

// 가상의 NMEA 파서 상태 머신 발췌 코드
uint8_t calculated_checksum = 0;
uint8_t received_checksum = 0;

void parse_char(char c) {
    switch (_state) {
        // ...
        case STATE_PAYLOAD:
            if (c == '*') {
                _state = STATE_CHECKSUM_1; // 페이로드 종료, 체크섬 수신 모드 돌입
            } else {
                // 매 문자마다 XOR 누적 연산 (O(1) 시간 복잡도)
                calculated_checksum ^= (uint8_t)c; 
                buffer[_buffer_idx++] = c;
            }
            break;
            
        case STATE_CHECKSUM_1:
            // 첫 번째 16진수 문자 ASCII를 바이트로 해석하여 상위 4비트에 시프트
            received_checksum = hex_to_byte(c) << 4;
            _state = STATE_CHECKSUM_2;
            break;
            
        case STATE_CHECKSUM_2:
            // 두 번째 문자를 해석하여 하위 4비트에 더함
            received_checksum |= hex_to_byte(c);
            
            // 최종 무결성 검증 (Validation)
            if (calculated_checksum == received_checksum) {
                // 검증 성공: 버퍼를 확정(Commit)하고 형변환 루틴으로 넘김
                decode_buffer();
            } else {
                // 검증 실패: 무음으로 버퍼를 파기(Drop)하여 EKF2 오염 방지
                reset_parser();
            }
            break;
    }
}
  • 위 코드 블록에서 보듯, PX4는 버퍼를 별도의 배열로 복사하여 for 루프를 도는 낭비를 저지르지 않는다. 한 바이트의 문자가 직렬 포트에 도착하는 바로 그 순간(In-place), 포인터를 전진시키면서 calculated_checksum ^= c; 단 한 줄의 인스트럭션만으로 누산기를 갱신한다.

0.3 XOR 로직 구조의 아킬레스건 (결함의 상쇄 한계)

NMEA에서 채택한 이 단순 무식한 XOR 누적 기법은 1980년대 구형 마이크로프로세서 시대에는 극강의 계산 트레이드오프(저비용 고효율)를 자랑했지만, 수백 MHz의 연산량을 손쉽게 감당하는 현대의 비행 제어기에서는 오히려 취약점으로 작용한다.

  • 짝수 비트 에러 상쇄(Cancel-out) 현상: 만약 패킷 중간에 강력한 모터 임펄스 노이즈가 강타하여, 정확히 두 바이트의 동일한 비트 위치에서 비트 플립(0->1, 1->0)이 동시에 발생했다고 가정해 보자. XOR의 수학적 성질(A \oplus B \oplus B = A)에 의해 두 에러는 서로를 무효화(Cancel-out)시키며 체크섬 결과에 아무런 영향을 미치지 않는다. 즉, 데이터는 명백히 오염되었으나 체크섬은 True를 반환해 버리는 대참사가 일어날 확률이 존재한다.
  • 바이너리 프로토콜로의 회기 당위성: PX4가 최신 GPS 라인업에서 NMEA를 후순위로 미루고 u-blox UBX 등 고유 바이너리 프로토콜을 우선 권장하는 이유 중 하나가 바로 이 허술한 데이터 검증 구조에 있다. UBX는 16비트 순환 다항식 방식의 견고한 Fletcher 체크섬을 보유하고 있어 상쇄 불가능한 오염을 거의 100%에 가깝게 포착해 내기 때문이다.