13.5.2.1. mavlink_receiver.cpp: MavlinkReceiver::handle_message_gps_rtcm_data() 메서드의 패킷 재조립(Reassembly) 유효성 검사 및 gps_inject_data uORB 토픽 퍼블리싱
QGroundControl(QGC) 등 관제 시스템이 하늘로 쏘아 올린 MAVLink 규격의 RTCM 보정 데이터(GPS_RTCM_DATA, 메시지 ID 233)가 무선 텔레메트리 안테나를 거쳐 PX4-Autopilot 운영체제의 심장부로 진입하는 첫 번째 관문은 바로 mavlink_receiver 모듈이다.
본 절에서는 MAVLink 패킷 파서(Parser)의 일선인 src/modules/mavlink/mavlink_receiver.cpp 버퍼 체인에서, handle_message_gps_rtcm_data() 메서드가 수신된 바이트 스트림을 어떻게 가공하고(Unpack), 드론 내부의 초고속 메시징 버스인 uORB 네트워크 위에 gps_inject_data 라는 이름표(Topic)를 달아 띄워 보내는지(Publish) C++ 클래스 메서드 단위로 정밀 분석한다.
1. MAVLink 데이터 버스와 수신기 메인 루프 (Parser Loop)
PX4 펌웨어가 텔레메트리 직렬 포트(Serial Port, 예: ttyS1, /dev/ttyS2)를 할당받으면, NuttX RTOS 기반의 mavlink_main 태스크(Task) 스레드는 해당 포트에 파일 디스크립터(fd) 인터럽트를 걸고 한 스레드 안에서 들어오는 바이트(Byte) 조각들을 끝없이 폴링(Polling)한다.
바이트가 어느 정도 모여 MAVLink 프레임 헤더(Header) 식별자인 페이로드 길이 검사와 16비트 검사합(Checksum, CRC16) 검증을 무사히 통과하면, 프레임이 유효한 MAVLink 구조체(C Struct) 하나로 확정되어 MavlinkReceiver::receive_message(mavlink_message_t *msg) 핸들러로 던져진다.
1.1 거대한 switch-case 분기
receive_message 내부에는 메시지 ID(msg->msgid)를 판별하는 수백 줄의 거대한 분기문이 존재한다. MSG_ID = 233 인 패킷이 잡히면 즉시 RTCM 전담 포장 박스인 handle_message_gps_rtcm_data(msg) 메서드가 발동된다.
2. handle_message_gps_rtcm_data() 메서드의 데이터 언패킹(Unpacking)
이 메서드는 컴파일러가 최적화(Optimization, -O2)하여 하드코딩한 헤더의 C 매크로 함수를 거쳐, 들어온 패킷을 $182\text{-bytes}$ 구조체(mavlink_gps_rtcm_data_t)로 끄집어낸다.
// src/modules/mavlink/mavlink_receiver.cpp (메서드 역참조 유사 로직)
void MavlinkReceiver::handle_message_gps_rtcm_data(mavlink_message_t *msg)
{
// 1. MAVLink 페이로드 데이터를 C 구조체로 메모리 디코딩 (Decoding)
mavlink_gps_rtcm_data_t rtcm_data;
mavlink_msg_gps_rtcm_data_decode(msg, &rtcm_data);
// 2. 단편화(Fragmentation) 플래그 및 버퍼 크기 안전성 검증
size_t payload_len = rtcm_data.len;
// len 필드가 구조체 용량 한계치(180)를 초과하여 넘치는지 오버플로우 방어(Bounds Check)
if (payload_len > sizeof(rtcm_data.data)) {
payload_len = sizeof(rtcm_data.data); // 180으로 트리밍(Trimming)
}
// (3. uORB 복사 및 퍼블리싱 단계로 진입)
}
여기서 가장 중요한 보안 방어 코드(Defense Code)는 2단계의 바이트 길이 트리밍(Trimming)이다. 알 수 없는 RF 혼선이나 QGC 버그로 인해 MAVLink의 len 필드에 200 이라는 값이 기입되어 넘어왔다면, 이후 memcpy 등을 돌릴 때 힙/스택 오버플로우(Memory Overflow)가 터져 기체가 즉시 재부팅되는 치명적인 크래시(Crash)가 일어난다. payload_len이 구조체의 가용 사이즈(180)를 넘어갈 경우 강제로 잘라내는 코드는 C++ 펌웨어 엔지니어링의 기본(Basic)이다.
3. 재조립(Reassembly) 없는 즉시 통과 모듈: gps_inject_data uORB 토픽 퍼블리싱 (Publish)
앞 절에서 누누이 강조했듯, PX4 MAVLink 파서는 QGC의 분할 전송 알고리즘(flags 를 이용한 단편화 송신)으로 쪼개진 180\text{ bytes}짜리 조각들을 한 덩어리로 붙이기 위한 거대한 임시 할당 버퍼(Reassembly Buffer)를 절대 유지하지 않는다. 들어온 조각(Fragment)은 단 1픽셀도 형태를 바꾸지 않고 이름표만 바꾼 채 다음 버스로 토스(Toss)되어야 한다.
이 목적을 위해 PX4 개발자들은 gps_inject_data_s 라는 uORB 주제(Topic) 구조체를 만들어 두었다.
// src/modules/mavlink/mavlink_receiver.cpp (uORB 퍼블리싱 로직)
// 3. uORB에 담을 구조체 인스턴스화
gps_inject_data_s inject_data{};
// uORB 인터페이스로 현재의 RTCMMavlink 길이와 플래그를 미러링(Mirroring)
inject_data.len = payload_len;
inject_data.flags = rtcm_data.flags;
// 4. 데이터 영역 메모리 복사 (memcpy)
memcpy(inject_data.data, rtcm_data.data, payload_len);
// (선택적) Device ID 매핑이 있다면 설정
inject_data.device_id = 0; // 0은 멀티플렉스 수신 혹은 주 장비(Primary)
inject_data.timestamp = hrt_absolute_time(); // 현재 하드웨어틱(Tick) 시간 각인
// 5. uORB 시스템으로 최종 발행 (Publish)
_gps_inject_data_pub.publish(inject_data);
}
3.1 펌웨어 내의 릴레이(Relay) 파이프라인
위 C++ 구문을 통해 바이트를 건네받은 주체는 다름 아닌 OS 스케줄러 영역 깊숙한 곳의 uORB 데몬이다.
MavlinkReceiver코드는_gps_inject_data_pub.publish(inject_data)한 줄을 실행한 후 자신의 임무(Task)에서 완전히 손을 떼고(Fire-and-Forget) 메모리 컨텍스트(Context)를 초기화하며, 다음 번 심박수(Heartbeat) 메시지를 처리하러 루틴의 처음으로 돌아가 버린다.- 이로 인해 MAVLink 모듈 내부에 찌꺼기가 남지 않으며, GPS 하드웨어와의 직렬 포트 인터페이스 처리가 밀렸는지 안 밀렸는지(Blocking) 수신단은 신경 쓸 필요가 없는 완벽한 소프트웨어 아이솔레이션(Isolation) 디자인이 성립된다.
이 uORB 토픽(gps_inject_data)은 비행 제어기 전체 프로세스 공간망(Space-net)으로 라우팅되며, 이 토픽을 눈빠지게 기다리고 있는 src/drivers/gps 폴더의 GPS 드라이버 스레드를 깨우는(Wake-up) 방아쇠(Trigger) 역할을 하게 된다. MAVLink의 영역은 우아하게 여기까지인 셈이다.