### 0.0.1 객체 지향 추상화: `GPSProvider` 추상 베이스 클래스(`virtual` 함수) 및 공통 데이터 인터페이스 설계

### 0.0.1 객체 지향 추상화: GPSProvider 추상 베이스 클래스(virtual 함수) 및 공통 데이터 인터페이스 설계

PX4의 gps_main 스레드는 직렬 포트가 뿜어내는 수천 바이트의 데이터들을 스스로 파싱하지 않는다. 백엔드에서 묵묵히 데이터를 가공할 프로토콜 파서(Protocol Parser) 객체들에게 모든 권한을 위임한다.
그러나 NMEA, U-Blox UBX, Emlid, Trimble 등 수많은 제조사(Vendor)들이 각자의 기이한 방식대로 패킷을 암호화하고 전송하는데, 메인 펌웨어는 어떻게 이들을 수십 개의 if-else 문살 없이 깔끔하게 통합하여 관장할 수 있을까?

그 해답은 C++ 객체 지향 프로그래밍(OOP)의 꽃인 추상화(Abstraction)와 다형성(Polymorphism)을 구현한 **GPSProvider 추상 베이스 클래스(Abstract Base Class)**라는 설계 패턴에 숨어있다.

0.1 GPSProvider 클래스의 기원(Origins)과 순수 가상 함수(Pure Virtual Functions)

src/drivers/gps/devices/src/gps_helper.h (버전에 따라 독립된 베이스 헤더) 트리에 정의된 GPSProvider (또는 GPSHelper) 클래스는 그 자체로는 아무런 파싱 능력이 없는 ‘뼈대’ 데이터 구조이다. 이 클래스는 모든 벤더 파서들이 의무적으로 구현해야만 하는 엄격한 계약(Contract)인 순수 가상 함수를 제정한다.

  • virtual int configure(unsigned &baudrate, GPSConfig &config) = 0;:
    포트가 열릴 때 가장 먼저 호출된다. 각 하위 클래스는 이 가상 함수를 상속받아, 자신만의 매직 패킷 프로빙 및 보드레이트 록인(Lock-in) 로직을 전개해야 한다.
  • virtual int receive(unsigned timeout) = 0;:
    가장 핵심이 되는 메인 폴링(Polling) 루틴 콜백이다. gps_thread_main은 메인 루프를 돌면서 이 함수 하나만을 무지성으로 호출한다. 하위 파서 칩셋이 내부적으로 상태 머신을 돌리든 버퍼를 버리든 상관하지 않고, 오직 반환값(예: 1=위치 갱신됨, 0=데이터 부족, -1=에러)만을 기다린다.

메인 프론트엔드는 이처럼 구체적인 타입(Ubx, Nmea 등)을 알 필요 없이 오로지 GPSProvider* 형태의 업캐스팅(Up-casting)된 부모 포인터만을 쥔 채로 가상 함수 테이블(vtable)을 거쳐 백엔드를 통제하는 완벽한 디커플링(Decoupling)을 성취해 낸다.

0.2 공통 데이터 인터페이스: sensor_gps_s 구조체의 역할

각각의 백엔드 파서 클래스(UbxGps, NmeaGps 등)가 receive() 함수 내부에서 우여곡절 끝에 벤더 고유의 패킷을 해체하여 위도, 경도, 속도를 얻어냈다 하더라도, 반환형이 다르면 메인 제어기가 이를 소비할 수 없다.

GPSProvider 클래스는 퍼블릭(Public) 영역에, PX4 시스템이 범용적으로 사용하는 uORB 메시지 포맷의 원형인 sensor_gps_s 구조체 포인터(혹은 레퍼런스)를 인터페이스로 제공한다.

  • 하위 파서들은 파싱에 성공한 즉시, 부모 스코프(Scope)에서 물려받은 이 구조체 인스턴스의 멤버 변수들(lat, lon, alt, vel_ned, eph, epv, timestamp 등)을 수작업으로 맵핑(Mapping) 시급히 갱신해야 한다.
  • 예를 들어 UBX 파서는 UBX-NAV-PVT의 10^7 배율 적용 정수를 읽어 밀리미터 단위로 환산한 뒤 공통 구조체에 쑤셔 넣고, NMEA 파서는 $GPGGA의 아스키(ASCII) 문자열을 strtod()로 실수 캐스팅하여 해당 멤버 변수에 대입한다.
  • 이러한 표준화된 깔때기(Funnel) 역할 덕분에, 프론트엔드 퍼블리셔(Publisher)는 단지 GPSProvider가 품고 있는 이 공통 구조체 데이터 한 덩어리를 복사하여 시스템 버스인 uORB 네트워크에 orb_publish()로 밀어 넣기만 하면 모든 임무가 끝난다.

0.3 Ardupilot AP_GPS 라이브러리와의 추상화 비교

Ardupilot 시스템 또한 AP_GPS 라이브러리를 통해 이와 매우 유사한 백엔드 계층화(Layering) 구조를 갖추고 있다.

  • Ardupilot의 경우 AP_GPS_Backend라는 베이스 클래스와 read() 가상 함수를 상속받은 AP_GPS_UBLOX, AP_GPS_NMEA 구체화 클래스들이 대응된다.
  • 이 둘(PX4 Provider vs Ardupilot Backend)의 구조적 철학은 **“개별 벤더의 프로토콜 오염을 메인 내법(Navigation) 코드로 전이시키지 않는다”**는 측면에서 소름 돋게 일치한다.
  • 다만 차이가 있다면 Ardupilot은 다중(Multi) 인스턴스를 관리하기 위한 프론트엔드(AP_GPS) 매니저가 백엔드들을 배열 구조로 통제하는 거대한 프레임워크 성격을 띠는 반면, PX4의 gps_main 드라이버는 NuttX 프로세스 모델 특유의 독립된 콘솔 단위 명령어로 쪼개져 task_spawn으로 훨씬 성기게(Loose) 파이프라인만 연결해 두는 마이크로 서비스(Micro-service) 형태에 가깝다는 점이다.

결과적으로 이 GPSProvider 추상 เบ이스 클래스 계층은 드론 코어 개발자와 제조사(OEM) 센서 드라이버 개발자 간의 간극을 완벽히 격리한 명작적(Masterpiece) 인터페이스 디자인이라 평가받는다.