### 0.0.1 UbxGps 클래스 소스 분석: 상태 머신(State Machine) 기반 1바이트씩(parse_char()) 비동기 처리하는 메모리 최적화 기법
드론 비행 제어기(FC)의 한정된 SRAM 리소스와 빡빡하게 짜인 실시간 스케줄링 환경에서, 문자열이나 바이트 스트림을 통째로 메모리에 복사해 두고 잘라 쓰는 파싱 기법은 커널 패닉(Kernel Panic)이나 지터(Jitter)를 유발하는 안티패턴(Anti-pattern)이다.
이 한계를 돌파하기 위해 PX4의 주력 파서인 UbxGps 클래스는 **비동기 1바이트 이벤트 구동형 상태 머신(Asynchronous 1-Byte Event-driven State Machine)**이라는 극단적인 최적화 구조를 취하고 있다.
그 중심에 있는 톱니바퀴 콜백, int UbxGps::parse_char(const uint8_t b) 함수의 작동 원리를 단계별로 분해해 본다.
0.1 버퍼 리스(Buffer-less) 파싱의 철학
기성 아두이노 라이브러리 등에서는 흔히 Serial.readBytes(buffer, length)로 덩어리를 받아 정규식(Regex)을 돌린다. 하지만 PX4의 UbxGps는 직렬 큐에서 바이트(uint8_t b)가 단 하나라도 튀어나오면 즉각적으로 parse_char(b)를 호출하여 판단 루프를 돌리고, 이 바이트를 저장할지 버릴지를 그 즉시 결정한다.
이 구조 덕분에 드라이버는 거대한 임시 수신 버퍼 배열(Array)을 필요로 하지 않으며, 메인 스레드는 바이트가 다 모일 때까지 기다리는 끔찍한 블로킹(Blocking)을 겪지 않는다. 데이터가 모자라면 return 0(아직 파싱 진행 중)을 던지며 우아하게 제어권을 반환한다.
0.2 switch-case 구문을 활용한 상태 전이(State Transition) 트랙
parse_char(b) 함수 내부는 거대한 switch (_decode_state) 구문으로 채워져 있으며, 바이트 하나가 유입될 때마다 현재 상태에 따라 다음 목적지가 결정된다.
UBX_DECODE_SYNC1/SYNC2(동기화 헤더 대기):
가장 기본이 되는 아이들링(Idling) 상태이다. 입력된 바이트b가0xB5(Sync 1)인지 확인한다. 맞다면 상태를SYNC2로 옮기고, 다음 바이트가0x62인지 살핀다. 이 매직 넘버(Magic Number) 쌍이 확인되어야만 이 바이트 스트림이 가비지(Garbage) 노이즈가 아닌 정상적인 UBX 메시지의 시작임을 확신한다.UBX_DECODE_CLASS/ID(메시지 종류 판별):
이후 연달아 들어오는 2바이트를 통해 이 메시지가 내비게이션 데이터(NAV-PVT)인지, 위성 상태(NAV-SAT)인지, 아니면 제어 응답(ACK-ACK)인지_rx_msg열거형 변수에 기억해 둔다.UBX_DECODE_LENGTH1/LENGTH2(페이로드 길이 획득):
UBX 프로토콜은 가변 길이이므로, 다음에 올 알맹이(Payload)가 몇 바이트짜리인지 16비트 리틀 엔디안(Little Endian)으로 읽어 들여_rx_payload_length변수에 세팅한다.UBX_DECODE_PAYLOAD(알맹이 수집 및 구조체 맵핑):
진정한 데이터 복사가 일어나는 유일한 구간이다. 설정된 길이만큼 루프(또는 반복 호출)를 돌며, C++의 구조체 포인터(예:ubx_payload_rx_nav_pvt_t*)의 메모리 주소 오프셋(Offset)에 들어오는 바이트b를 차곡차곡memcpy하듯 직접 써넣는다(Direct Placement). 별도의 중간 버퍼를 거치지 않으므로 복사 오버헤드(Overhead)가 제로(0)에 수렴한다.UBX_DECODE_CHKSUM1/CHKSUM2(무결성 검증):
마지막으로 페이로드 수집이 끝나면 꼬리에 붇은 16비트 플레처 체크섬 연산 결과가 모듈이 보내온 체크섬과 일치하는지 비교한다. 일치한다면return 1(파싱 완료 및 메시지 유효)을 반환하여, 상위 스레드가 이 데이터를 uORB로 퍼블리싱하도록 격발시킨다. 만약 여기서 체크섬이 어긋나면 단호하게 지금까지 모든 데이터를 포기하고 상태를 다시SYNC1으로 되돌린다.
0.3 실시간 제어 스케줄러(Scheduler) 친화적 설계
이러한 상태 머신 기반 파싱의 가장 큰 혜택은 **“예측 가능한 최악 실행 시간(WCET, Worst-Case Execution Time)”**을 보장한다는 점이다.
parse_char()함수는 반복문(While, For) 없이 오직O(1)의 시간 복잡도를 가지는switch-case와 간단한 산술 연산, 그리고 대입 연산만으로 구성되어 있다.- 따라서 백그라운드 스레드가 아무리 빠르게, 혹은 아무리 미친 듯이 조각난(Fragmented) 바이트 단위로 인터럽트를 발생시키더라도, 파서의 CPU 틱(Tick) 소비량은 항상 납작하게 일정하게 유지된다.
이는 결국 드론의 메인 EKF2 추정기와 자세 제어(Rate Control) PID 루프가 GPS 드라이버의 돌발적인 CPU 리소스 점유(Spike)에 의해 간섭받지 않고, 수천 헤르츠(Hz)의 고속 루프를 매우 안정적으로 평탄하게 유지할 수 있게 만드는 소프트웨어 엔지니어링의 정수라 할 것이다.