### 0.0.1 `mavlink_messages.cpp`의 스트림 클래스에서 uORB `sensor_gps` 데이터를 `GPS_RAW_INT` 패킷으로 변환하는 직렬화(Serialization) 과정

### 0.0.1 mavlink_messages.cpp의 스트림 클래스에서 uORB sensor_gps 데이터를 GPS_RAW_INT 패킷으로 변환하는 직렬화(Serialization) 과정

uORB 구조체를 MAVLink 데이터 스트림으로 포맷팅하는 실제 기계적 변환 과정은 src/modules/mavlink/mavlink_messages.cpp 파일 내에 정의된 개별 스트림 클래스들에 의해 주도된다. GPS의 경우, MavlinkStreamGPSRawInt 라는 전담 클래스가 이 직렬화(Serialization)의 최전선에 서 있다.

본 절에서는 단일 GPS 데이터를 MAVLink #24번 GPS_RAW_INT 패킷으로 매핑(Mapping)하고 조립해 내는 소스 코드 레벨의 알고리즘을 분석한다.

0.1 전용 스트림 클래스: MavlinkStreamGPSRawInt

PX4의 mavlink_messages.cpp를 열어보면, 각 MAVLink 메시지 스트림은 MavlinkStream이라는 순수 가상 클래스(Pure Virtual Class)를 상속받아 구현되어 있음을 알 수 있다.

  • MavlinkStreamGPSRawInt 클래스는 부팅 시 MAVLink 모듈 초기화 과정에서 인스턴스화되며, 내부적으로 sensor_gps uORB 토픽을 구독(Subscription)하는 핸들(_gps_sub)을 보유한다.
  • 가장 핵심이 되는 메서드는 가상 함수인 send() 이다. MAVLink 송신 루프가 “GPS 데이터를 보낼 차례“라고 스케줄러를 통해 신호를 주면 이 send() 함수가 트리거(Trigger)된다.
  • 이 함수가 호출되면 가장 먼저 _gps_sub.update(&gps)를 실행하여 uORB 버퍼에 쌓여있던 최신 sensor_gps_s 구조체 데이터를 로컬 변수(gps)로 복사해 온다.

0.2 필드 대 필드 매핑(Field-to-Field Mapping)

가져온 sensor_gps_s 구조체의 데이터는 MAVLink C 헤더(mavlink_msg_gps_raw_int.h)에 정의된 mavlink_gps_raw_int_t 구조체로 옮겨 담겨야 한다. 이 과정은 다음과 같은 정교한 스케일링(Scaling) 및 단위(Unit) 변환을 수반한다.

  • Timestamp: uORB의 마이크로초(\mu\text{s}) 단위 timestamp는 MAVLink의 규격에 맞춰 부팅 후 경과 시간(Time since system boot)으로 변환되어 time_usec 필드에 채워진다.
  • Fix Type: 3D Fix, DGPS, RTK 상태를 나타내는 gps.fix_type (0~6의 값)은 직관적으로 MAVLink의 fix_type 필드에 1:1로 매핑된다.
  • 위도/경도/고도: 이미 10^7 로 스케일링 된 lat, lon 값은 그대로 복사되며, 미터(m) 단위의 고도는 밀리미터(mm) 단위인 alt 필드와 매핑된다. 고도 타원체 기준 값(Ellipsoid)인지 해수면 기준 값(MSL)인지에 유의하여 변환된다.
  • 신뢰성 지표 (epv, eph): uORB에서 미터(m) 단위 실수(float)로 존재했던 수평/수직 분산 오차는 MAVLink GPS_RAW_INT 규격에 따라 센티미터(cm) 단위의 정수(uint16_t)로 스케일 변환된 후 eph, epv 필드에 각각 캐스팅(Casting)되어 들어간다. 만약 값이 UINT16_MAX를 초과하면 최댓값으로 클램핑(Clamping) 처리한다.
  • 속도 (Velocity): NED 좌표계의 북, 동, 하 속도 벡터(vel_n_m_s 등)는 스칼라 형태의 절대 지면 속도(cm/s 단위)와 진행 방위각(Course Over Ground, 0 \sim 359.99^{\circ} 의 센티 디그리 단위)인 vel, cog 필드로 삼각함수(아크탄젠트와 피타고라스 정리) 연산을 거쳐 변환된다.
  • 위성 개수: satellites_used 필드는 255(0xFF)를 넘지 않도록 안전하게 satellites_visible 필드로 매핑된다.

0.3 메시지 엔코딩 및 버퍼 송출 (mavlink_msg_gps_raw_int_send_struct)

모든 필드 매핑이 완수되어 mavlink_gps_raw_int_t 구조체가 빽빽하게 채워지면, 코드는 송신을 위한 마지막 엑셀을 밟는다.

  1. PX4는 MAVLink C 라이브러리가 제공하는 자동 생성 인라인 함수인 mavlink_msg_gps_raw_int_send_struct()를 호출한다.
  2. 이 함수는 채워진 구조체 포인터를 받아, 구조체의 빈 공간(Padding)을 밀어버리고 MAVLink v1.0 혹은 v2.0 규격에 맞는 직렬화(Serialization)를 수행한다. 즉, 리틀 엔디안 방식의 바이트 배열(Byte Array)로 납작하게 누르는 것이다.
  3. 그다음 데이터 덩어리 앞뒤로 0xFD (MAVLink v2 마커), 시퀀스 ID, 시스템 ID, 컴포넌트 ID가 직조된 헤더(Header) 10바이트와, 메시지 무결성 검증을 위한 2바이트 CRC 체크섬(Checksum) 꼬리표를 강제로 이어 붙인다.
  4. 마지막으로 조립이 완료된 이 패킷은 UART 포트나 UDP 소켓 버퍼(Tx Buffer)로 밀어 넣어지며, 하드웨어 인터럽트를 타고 QGroundControl을 향한 전파의 바다로 다이빙한다.

이 일련의 mavlink_messages.cpp 직렬화 알고리즘 덕분에, 드론 컨트롤러 내부의 추상화된 이기종 데이터들은 전 세계 어디서나 통용되는 표준 통신 규격으로 변환되어 GCS 화면의 계기판을 부드럽게 움직일 수 있게 된다.