13.5.1.1.1. `flags` (단편화 식별 비트), `len` (페이로드 길이), `data` (최대 180바이트 버퍼) 필드의 메모리 패킹 및 엔디안(Endian) 처리

13.5.1.1.1. flags (단편화 식별 비트), len (페이로드 길이), data (최대 180바이트 버퍼) 필드의 메모리 패킹 및 엔디안(Endian) 처리

통신 프로토콜 설계에 있어 가장 기초적이면서도 치명적인 버그를 양산하는 주범은 구조체의 ’메모리 패킹(Memory Packing)’과 전송 시의 ‘엔디안(Endianness)’ 문제이다. MAVLink 프로토콜의 GPS_RTCM_DATA (메시지 ID 233) 패킷은 오직 1바이트 크기의 자료형(uint8_t)만으로 구성된 아주 독특한 자료 구조를 갖는다.

본 절에서는 이 패킷의 핵심 필드인 flags, len, data 가 C/C++ 컴파일러(Compiler) 수준에서 어떻게 메모리에 적재(Packing)되며, 이기종 아키텍처(예: x86 바탕의 QGC 노트북과 ARM Cortex 기반의 픽스호크(Pixhawk) 비행 제어기) 간의 바이트 순서(Byte Order) 불일치 문제를 MAVLink가 어떻게 원천적으로 회피하고 있는지 심층 분석한다.

1. 메모리 패드(Padding) 프리(Free) 구조체와 __attribute__((packed))

일반적으로 C/C++ 컴파일러는 CPU의 메모리 접근 구조 최적화를 위해 구조체 변수들 사이에 보이지 않는 빈 공간(Padding Byte)을 삽입한다. 32\text{-bit} 아키텍처에서는 주소가 4의 배수로 떨어지도록 패딩을 넣는 것이 관례이다.

하지만 MAVLink 패킷은 무선 통신 대역폭 절약을 위해 이러한 패딩을 단 1\text{ byte}도 허락하지 않는 ‘압착된(Packed)’ 자료구조를 강제한다.

// MAVLink 자동 생성 헤더팩 파일 내 압착된 구조체 선언 부호
MAVLINK_MESSAGE_INFO_GPS_RTCM_DATA
#pragma pack(push, 1) // 패킹 정렬 기준을 1바이트로 강제함
typedef struct __mavlink_gps_rtcm_data_t {
    uint8_t flags;       // Offset: 0 bytes
    uint8_t len;         // Offset: 1 bytes
    uint8_t data[180];   // Offset: 2 bytes
} mavlink_gps_rtcm_data_t;
#pragma pack(pop)

이 기법(#pragma pack(push, 1) 또는 gcc의 __attribute__((packed))) 덕분에 mavlink_gps_rtcm_data_t 구조체의 크기는 논리적인 합(1+1+180)과 물리적인 크기(sizeof)가 동일한 **182\text{ bytes}**로 정확히 일치하게 된다. 이 패킹된 C 구조체를 포인터 구문 (uint8_t *)&rtcm_data 타입 캐스팅을 통해 바로 직렬 포트로 밀어 넣을 수 있는 구조적 이점을 누린다.

2. 엔디안(Endianness) 독립적인 설계 철학

네트워크 통신의 최대 적인 바이트 순서(Byte Order) 문제는 2\text{ bytes} 이상의 멀티바이트(Multi-byte) 정수, 즉 uint16_tuint32_t 를 다룰 때 발생한다.

  • 빅 엔디안(Big-endian, 네트워크 헤더 기준): MSB(가장 큰 자리수)가 메모리 앞쪽(낮은 주소)에 저장.
  • 리틀 엔디안(Little-endian, 인텔 x86 및 대부분의 ARM 프로세서): LSB(가장 작은 자리수)가 메모리 앞쪽에 저장.

만약 RTCM 데이터가 MAVLink 구조체 안에서 uint32_t data[45] 형태로 선언되었다면 어떨까? 엔디안이 다른 QGC 노트북과 픽스호크 칩셋 간 통신에서 바이트 순서가 사분오열되어 데이터가 통째로 훼손되었을 것이다. htonl() (Host to Network Long) 함수 래핑 등 귀찮은 변환 작업도 수행해야 한다.

하지만 mavlink_gps_rtcm_data_t 구조체는 이 고민을 역발상으로 날려버렸다.

2.1 1바이트 자료형(uint8_t)만을 고집하는 엔디안 회피

  • flags 필드: 1바이트 (uint8_t)
  • len 필드: 1바이트 (uint8_t)
  • data 배열: 1바이트의 집합 (uint8_t[180])

구조체의 모든 원소가 정확히 1바이트 슬라이스(Slice)로 선언되어 있기 때문에, 이 데이터 덩어리(182\text{ bytes})를 메모리 가장 첫 주소부터 바이트 단위로 긁어내어 통신선(UART/Radio)에 써내려 간다면 수신 측에서도 바이트 배열 그대로 재조립이 가능하다.

요컨대, 1바이트씩 쪼개진 바이트 버퍼의 배열 형태는 엔디안이 개입할 여지(순서를 바꿀 자리)조차 없으므로 ‘엔디안 독립형(Endian-agnostic)’ 자료구조가 성립된다. 이 우아한 하부(Low-level) 디자인 덕분에 QGC에서 출발한 원시 RTCM 바이트 슬라이스는 텔레메트리를 날아 드론의 CPU를 통과해 최종 목적지인 GPS 하드웨어 칩셋(예: u-blox F9P RX 핀)에 도달하기까지, 그 어떤 hton() 변환이나 바이트-스와핑(Byte-swapping) 필터 로직 없이 다이렉트 패스스루(Direct Pass-through)될 수 있는 것이다.

3. 필드 역학 구조의 프로토콜 오버헤드 한계

1바이트 자료형은 통신 지연 속도를 최저로 낮추고 엔디안 문제를 회피하지만, 태생적인 단위 한계를 띤다.

3.1 len 변수의 구조적 페이로드 상한: 255 바이트

len 변수는 uint8_t 이며, 나타낼 수 있는 최대 수치는 2^8 - 1 = 255 이다. 만약 한 회차(Chunk)에 전송해야 할 RTCM 바이트 크기를 180바이트보다 크게 설정하고 싶더라도, len 변수가 표현할 수 있는 한계치(255) 그리고 MAVLink 버전1 프로토콜 자체의 페이로드 한계치(255\text{ bytes})라는 구조적 천장에 막히고 만다. 이러한 이유로 data 배열의 슬라이스 크기는 관행적으로 255\text{ bytes} 미만, MAVLink 규격에서는 딱 180\text{ bytes} 크기로 정립(Fix)되었다.

결론적으로 MAVLink 개발진은 GPS_RTCM_DATA 프로토콜을 디자인함에 있어, 2\text{ bytes} 이상의 고급 자료형과 화려한 C/C++ 파서를 과감히 포기했다. 그 대신 \text{sizeof = 1 byte} 짜리 원시 uint8_t 블록들과 강제형 구조체 팩킹(#pragma pack 1)을 통해 아키텍처 종속성을 완벽히 끊어냈다. 이것은 수십 만 개의 PX4 드론이 리눅스, 윈도우, NuttX RTOS 등 플랫폼을 막론하고 그 어떤 바이트 왜곡 없이 센티미터 수준의 통계적 정밀도를 유지할 수 있는 기저의 숨은 공신이라 할 수 있다.