### 0.0.1 벤더별 프로토콜 파서(Parser) 구현 클래스 및 다형성(Polymorphism)

### 0.0.1 벤더별 프로토콜 파서(Parser) 구현 클래스 및 다형성(Polymorphism)

앞 절에서 GPSProvider 추상 클래스가 제공하는 표준화된 공통 인터페이스 계층을 확인했다. 이제 이 인터페이스의 제약을 철저히 준수하면서도, 뒤에서는 각 제조사(Vendor) 고유의 복잡한 통신 규격과 싸우며 데이터를 정제해 내는 백엔드 파서(Backend Parser) 클래스들의 다형성(Polymorphism) 구현 구조를 살펴본다.

PX4 src/drivers/gps/devices/src/ 디렉토리를 열어보면, 마치 여러 브랜드별 드라이버가 질서 정연하게 도열해 있는 듯한 소스 코드 트리 구조를 목격할 수 있다.

0.1 UbxGps (U-Blox 바이너리 파서): 1바이트 이벤트 구동 상태 머신

ubx.cppubx.h에 구현된 UbxGps 클래스는 PX4 환경에서 가장 거대하고 복잡하며, 동시에 가장 광범위하게 쓰이는 주력(Flagship) 파서이다.

  • 스위스의 U-Blox 사가 제정한 UBX 바이너리 프로토콜은 가변 길이 페이로드, 클래스 및 ID 계층, 그리고 16비트 플레처 체크섬(Fletcher Checksum)이라는 깐깐한 프레임을 덧씌워 통신한다.
  • UbxGps 인스턴스는 한꺼번에 바이트 뭉치를 넘겨받아 버퍼를 통째로 뒤적이는 구시대적 정규표현식 파싱을 거부한다. 대신 극단적으로 메모리를 아끼고 파싱 오버헤드를 줄이기 위해, parse_char()라는 메서드 안쪽에 촘촘하게 구성된 **상태 머신(State Machine)**을 내장하고 있다.
  • 이는 메인 폴링 루프에서 바이트가 들어올 때마다 상태를 전이시키는 방식이다. 매직 넘버 대기 상태(UBX_SYNC1) \rightarrow 클래스 아이디 판별(UBX_CLASS) \rightarrow 페이로드 수집(UBX_PAYLOAD) \rightarrow 체크섬 검증(UBX_CHECKSUM) 순으로 이어지는 톱니바퀴 메커니즘은, 단 1바이트의 잉여 루프나 메모리 복사도 허용하지 않는 무결점 비동기 처리를 실현한다.

0.2 NmeaGps (ASCII 정규식 파서): 하위 호환성의 수호자

가장 원시적인 텍스트 기반 NMEA 0183 통신을 처리하는 nmea.cppNmeaGps 클래스는 UBX 파서와는 완전히 다른 방향성을 지닌다.

  • 상태 머신의 상태는 $GPGGA$GPRMC 같은 문자열 헤더를 감지하는 데 집중되어 있으며, 쉼표(,)를 구분자로 삼아 각 토큰(Token)을 잘라내는 로직이 주를 이룬다.
  • 문자열을 실수(Float) 단위나 정수형으로 변환하는 strtod(), atoi() 류의 C 표준 라이브러리 함수가 다수 호출되므로 파싱 코스트(Cost) 자체는 UBX보다 비싸다. 그러나 수천 종에 달하는 저가형 모듈이나 타사 이기종 센서(예: 해양용 GPS)와의 인터페이스를 여는 범용 드라이버로서, PX4-Autopilot의 하위 호환성(Backward Compatibility)을 강력하게 지탱해 준다.

0.3 고정밀(RTK) 특화 파서 클래스들: AshtechGps, EmlidReach, Trimble

수 센티미터(cm) 단위의 이동체 측위가 필요한 농업, 측량, 광역 매핑(Mapping) 산업에 드론이 투입되면서, PX4는 하이엔드(High-end) RTK 수신기들의 독자 프로토콜을 파싱하는 다형성 객체들을 맹렬하게 추가해왔다.

  • ashtech.cpp (AshtechGps): 구형 항공 특수 텔레메트리 파서.
  • emlid_reach.cpp (EmlidReach): 최근 오픈소스 진영에서 RTK의 대중화를 이끈 Emlid 사의 Reach 시리즈 수신 전용 어댑터.
  • 그 밖에 mtk.cpp, sbf.cpp (Septentrio), rtcm.cpp 등이 각자의 receive() 가상 함수 내부에서 제조사의 바이너리 스펙 문서를 바이트 코드 단위로 번역해 내는 역할을 전담한다.

0.4 팩토리 패턴(Factory Pattern)을 통한 동적 다형성 바인딩

이 수많은 파서 클래스들을 gps_main 드라이버 스레드는 도대체 어떻게 선별하여 마운트시킬까?
여기서 C++의 동적 런타임 결정 구조인 팩토리(Factory) 개념과 다형성이 빛을 발한다.

사용자가 gps start -p ubx 라고 명시적으로 프로토콜을 지정하면, 포인터 생성 단계에서 g_dev = new UbxGps(...) 처럼 직접 하위 클래스로의 할당이 일어난다. 그러나 기본 옵션이나 자동 감지(Auto) 상태일 경우, 드라이버 메커니즘은 NmeaGpsUbxGps 등의 객체를 차례대로 넣었다 뺐다 하며 매직 패킷 프로빙을 실시하고, 성공적으로 동기화가 이루어진 객체 포인터만을 메인 GPSProvider* helper 레퍼런스에 업캐스팅하여 록인(Lock-in)시켜 버린다.

어떤 벤더 파서 클래스가 채택되든 메인 코드는 털끝 하나 수정되지 않으며, 단지 helper->receive() 구문만 묵묵히 반복될 뿐이다. 이것이 바로 PX4 센서 아키텍처가 전 세계 제조사 통신망의 홍수 속에서도 튼튼하게 버틸 수 있는 구조적 뼈대의 위력이다.