13.1.3.1. RTCM 전송 프레임(Frame) 구조 및 바이트 패킹(Byte Packing) 원리
정밀 측위를 위한 RTCM v3 (RTCM 10403.3) 메시지는 통신 채널 상의 트래픽을 최소화하기 위해 극도로 압축된 바이너리 비트 스트림(Bit Stream) 형태로 설계되어 있다. 이로 인해 데이터를 디코딩하는 과정은 통상적인 데이터베이스 프로토콜처럼 단순히 바이트 단위로 memcpy 연산을 수행하는 구조체 기반 파싱(C/C++ Struct Casting) 방식으로는 해결 불가능하며, 비트 시프트(Bit Shift)와 마스킹(Masking)을 수반하는 복잡한 바이트 패킹(Byte Packing) 알고리즘이 필수적이다.
본 절에서는 PX4-Autopilot 및 GNSS 드라이버 아키텍처 수준에서 RTCM 전송 프레임을 어떻게 해석하고, 가변 형태의 비트 필드를 어떻게 메모리에 적합한 스칼라 데이터 타입으로 추출해 내는지 물리적 메모리 구조 측면에서 분석한다.
1. RTCM 3.x 프레임(Frame)의 비트 레벨(Bit-level) 구조
물리적 전송 층을 거쳐 UART, I2C, 혹은 USB를 통해 비행 제어기(Flight Controller) 내의 메모리 버퍼로 유입되는 RTCM 3.x 데이터 프레임은 크게 3개의 헤더(Header)/메타 파트와 1개의 페이로드(Payload) 파트로 나뉜다. 이들은 총길이가 바이트 단위로 결정되는 전송 규격을 갖지만, 그 안의 필드들은 비트 단위로 구성된다.
block-beta
columns 8
space:4
block:Frame
columns 8
Preamble["Preamble\n(8 bits)\n0xD3"]
Reserved["Reserved\n(6 bits)"]
Length["Length\n(10 bits)"]
Payload["Data Message Payload\n(N x 8 bits)"]
CRC["CRC-24Q\n(24 bits)"]
end
수신 버퍼 안착 시, 이 데이터는 uint8_t (Unsigned 8-bit Integer) 배열로 간주된다.
- Preamble (8-bit): 무조건
1101 0011(0xD3) 값을 가지며, 프레임 동기화(Frame Synchronization)를 위해 사용된다. - Reserved (6-bit): 현재 사용되지 않으며,
000000을 유지한다. - Length (10-bit): 이 필드는 페이로드의 바이트 킬이를 가리킨다. 프레임의 전체 길이는 Length \text{ value} + 6 \text{ bytes} 이다.
- Data Payload: 각 RTCM 메시지 타입(Type 1005, Type 1074 등)에 따라 구조가 동적으로 변하는 비트 필드 스트림이다.
- CRC (24-bit): 전체 메시지 무결성을 위한 검증 코드이다.
2. 바이트 패킹(Byte Packing)의 이론적 접근
2.1 비트 스트림의 비참조성(Unaligned Bit Fields) 문제
C/C++ 언어에서 변수(Variable)들은 기본적으로 바이트(8비트) 단위로 어드레스(Address)가 배정된다. 그러나 RTCM 페이로드 내부의 데이터는 예를 들어 “메시지 식별 번호 (12비트)”, “기준국 ID (12비트)”, “GPS Epoch 시간 (30비트)” 등으로 정의된다. 이러한 비트 필드는 메모리 주소 체계와 어긋나는 오프셋(Offset)에 존재하게 되므로 개별적인 비트 추출 알고리즘을 사용해야 한다.
packet-beta
title 바이트 경계를 가로지르는 비트 필드 매핑 예시
0-7: "Byte 0 (Message ID 상위 8비트)"
8-11: "Byte 1 (상위 4비트: Msg ID 하위)"
12-15: "Byte 1 (하위 4비트: Base ID 상위)"
16-23: "Byte 2 (Base ID 하위 8비트)"
위 다이어그램에서 볼 수 있듯이, 12비트짜리 “Message ID“를 디코딩하려면 Byte 0의 8비트 전체와 Byte 1의 상위 4비트를 추출하여 병합해야 한다. 이것이 이른바 ‘비트 언패킹(Bit Unpacking)’ 과정이다.
2.2 엔디안(Endianness) 및 비트 오더링(Bit Ordering)
RTCM 데이터는 네트워크 바이트 오더링(Network Byte Order)과 같이 **MSB(Most Significant Bit)**가 가장 먼저 전송되는 빅-엔디안(Big-Endian) 방식을 취한다. PX4-Autopilot 소프트웨어가 동작하는 ARM Cortex-M 프로세서는 리틀-엔디안(Little-Endian) 구조이므로, 비트를 시프트(Shift)하여 변수에 삽입할 때 이 엔디안 차이를 역전시켜야 하는 주의가 필요하다.
3. PX4/드라이버 내 비트 파싱 소스 코드 레벨 구조 분해
이러한 비트 패킹/언패킹을 효율적으로 수행하기 위해, GPS 계열 드라이버나 QGroundControl 내부 코드, 그리고 MAVLink 생태계 모듈들은 비트 추출 함수를 자체적으로 구현하거나 지원 라이브러리를 사용한다.
디코더 엔진은 메모리에 올라간 바이트 포인터(buffer)와 현재 읽고 있는 비트의 포인터 오프셋(bit_offset) 상태를 유지하는 커서(Cursor) 객체를 사용한다.
다음은 임의의 바이트 스트림에서 지정된 길이(num_bits, 최대 32비트)만큼 데이터를 추출해내는 전형적인 커서 파싱의 C/C++ 알고리즘 예시이다.
// 바이트 배열 기반 비트 추출 및 이동 알고리즘 예시
uint32_t extract_bits(const uint8_t *buffer, uint32_t &bit_offset, uint8_t num_bits)
{
uint32_t value = 0;
for (uint8_t i = 0; i < num_bits; ++i) {
// 현재 비트의 위치를 계산: 바이트 인덱스와 바이트 내 비트 인덱스
uint32_t byte_idx = bit_offset / 8;
uint8_t bit_idx = 7 - (bit_offset % 8); // RTCM은 MSB-first(좌측 우선)로 패킹됨
// 해당 비트를 추출
uint8_t bit_val = (buffer[byte_idx] >> bit_idx) & 0x01;
// 추출된 비트를 결과 변수의 적절한 위치(왼쪽 시프트)에 삽입
value = (value << 1) | bit_val;
// 커서 이동
bit_offset++;
}
return value;
}
위의 함수 모듈은 이해를 돕기 위해 비트 단위 직렬 루프로 설명되었으나, PX4 내의 실제 최적화된 드라이버(예: src/drivers/gps 내의 u-blox 모듈)는 CPU 사이클(CPU Cycle)을 최소화하기 위해 바이트 단위의 <<, >>, &, | 논리 연산자를 덩어리로 묶어 처리한다.
예를 들어 10비트를 읽어야 할 경우, 8비트 바이트 하나를 통째로 읽어 시프트한 뒤, 다음 바이트에서 2비트만 마스킹하여(Masking) 결합하는 수식을 전개한다. 이 최적화 로직은 수백, 수천 개의 위성 궤도 및 의사 거리 정보가 포함된 거대한 MSM(Multiple Signal Messages) 패킷을 밀리초(ms) 단위로 처리하는 연산 지연 없는 Real-Time 환경 구성을 가능하게 만든다.
4. 구조체 캡슐화를 통한 파서의 모듈화
최종적으로 비트 스트림에서 추출된 데이터는 제어기 내부의 메모리 구조체에 스칼라 데이터(정수형, 부동소수점형)로 스케일링(Scaling) 및 캐스팅(Casting)되어 저장된다.
RTCM 프로토콜은 단순 정수뿐만 아니라, 오프셋과 스케일 팩터(Scale Factor)를 지정하여 해상도를 정의한다. 예를 들어 “GNSS 위성 간의 위상 거리 오차” 필드가 0.0001 미터 단위 시스템이라면 추출된 정수값에 0.0001 을 곱하여 실제 물리량인 float 타입 변수로 변환한다.
이러한 기법은 메모리 대역폭이 극도로 제한되었던 과거 항공 및 군사 우주 산업 초창기의 텔레메트리(Telemetry) 표준 방식에서 유래하였으며, 오늘날 무인 시스템 산업인 PX4-Autopilot 및 다양한 자율 에이전트 시스템(Autonomous Agents System)에서도 통신 안정성을 최대로 끌어올리기 위한 근간 팩시밀리로 작동하고 있다.