13.5.4.1. Mission Planner의 RTK Injector 모듈(C# 기반) 동기식 직렬 포트 스트리밍 스레드와 QGroundControl(C++ Qt 프레임워크) 비동기 시그널/슬롯(Signal/Slot) 이벤트 루프 성능 비교

13.5.4.1. Mission Planner의 RTK Injector 모듈(C# 기반) 동기식 직렬 포트 스트리밍 스레드와 QGroundControl(C++ Qt 프레임워크) 비동기 시그널/슬롯(Signal/Slot) 이벤트 루프 성능 비교

드론의 텔레메트리(Telemetry) 링크를 통해 지상에서 하늘로 막대한 양의 데이터를 밀어 넣는 행위는 좁은 병목을 통과하는 모래시계와 같다. Mission Planner(MP)와 QGroundControl(QGC)은 동일한 베이스 스테이션(Base Station) 하드웨어(e.g. u-blox F9P 칩셋)를 USB로 꽂아도, 이를 백그라운드 프로세스에서 읽어내어 무선 모뎀으로 배달하는 방식에 있어 완전히 상반된 소프트웨어 스레딩(Threading) 모델을 채택하고 있다.

본 절에서는 Ardupilot 진영의 C#/.NET 기반 Mission Planner가 구사하는 동기식(Synchronous) 직렬 포트 스트리밍 스레드와, PX4 진영의 C++ Qt 기반 QGroundControl이 자랑하는 비동기(Asynchronous) 이벤트 드리븐(Event-driven) 시그널/슬롯(Signal/Slot) 성능의 아키텍처적 차이와 스펙트럼(Spectrum)을 낱낱이 파헤친다.

1. Mission Planner (Ardupilot 진영): C# 기반 동기식 블로킹(Blocking) I/O

Mission Planner의 RTK Inject 플러그인 모듈은 C#의 고전적인 윈도우 폼(WinForms) 스레딩 패러다임을 충실하게 따른다.

베이스 스테이션이 연결되는 COM 포트를 관리하기 위해 MP는 백그라운드 워커 스레드(BackgroundWorker 또는 System.Threading.Thread)를 무한 루프(while(true)) 형태로 생성한다.

// Mission Planner 측의 RTK 주입기 유사 스트리밍 로직 (C#)
private void RtkThreadLoop() {
    while (_isRunning) {
        // 1. 시리얼 포트에서 1바이트라도 들어올 때까지 동기적으로 멈춤 (Blocking)
        int bytesToRead = baseSerialPort.BytesToRead;
        if (bytesToRead > 0) {
            byte[] buffer = new byte[bytesToRead];
            baseSerialPort.Read(buffer, 0, bytesToRead);

            // 2. 읽은 데이터를 곧바로 MAVLink 패킷으로 포장
            MAVLink.mavlink_gps_rtcm_data_t rtcm = new MAVLink.mavlink_gps_rtcm_data_t();
            // (180바이트 분할 로직 실행)

            // 3. MAVLink 스트림이 쓰는 공유 통신 포트로 동기식 쓰기 (Locking)
            lock (MainV2.comPort.BaseStream) {
                MainV2.comPort.sendPacket(rtcm, ...);
            }
        }
        // 4. 스레드 CPU 점유율 폭주 방지를 위한 강제 슬립 (Sleep)
        System.Threading.Thread.Sleep(1); 
    }
}

1.1 장점: 극도의 직관성과 구현의 단순함

C#의 동기적 Read/Write 메서드는 코드가 위에서 아래로 물 흐르듯 실행되므로, 개발자가 데이터 파이프라인(Data Pipeline)을 설계하고 디버깅하기가 직관적이다. 포트가 끊어지면 바로 예외(Exception)가 잡히는 등 트러블슈팅(Troubleshooting)이 쉽다.

1.2 단점: Lock 병목과 지연시간 추이 상승 (Jitter)

베이스 스테이션(Base)에서 초당 3 \sim 4\text{KB}의 RTCM3 스파이크(Spike)가 터질 때, 위 루프는 텔레메트리로 향하는 MainV2.comPort (메인 MAVLink 송신 포트)에 엄청나게 잦은 락(lock)을 건다.
Mission Planner의 메인 스레드가 드론에게 조이스틱 명령(RC Override)을 내리려 할 때, 이 RTK 스레드가 쓰기 권한(Lock)을 쥐고 스레드를 Sleep() 상태에 빠뜨리거나 대량의 뭉텅이를 토해내고 있다면 C# 가비지 컬렉터(Garbage Collector)의 불확실한 수거(Pause) 현상 타임아웃 딜레이와 결합하여 심한 **응답 지연(Jitter)**이 도출된다. 만일 텔레메트리 포트가 고의로 포화(Saturation)를 겪을 시 스트림 자체가 막혀버려(Blocked) UI 창 전체가 일시 정지(Freeze)하는 버그를 겪을 수도 있다.

2. QGroundControl (PX4 진영): C++ Qt 비동기 시그널/슬롯(Signal/Slot) 이벤트 루프

QGC는 C++과 크로스 플랫폼 Qt(큐티) 프레임워크의 걸작인 **비동기 이벤트 루프(Event Loop)**를 활용한다.
QGC에서 베이스 안테나가 꽂히면 스레드가 while 루프를 돌면서 Read 함수로 대기하지 않는다.

대신 Qt의 비동기 소켓/직렬 통신 클래스(QSerialPort)가 운영체제 커널(Kernel)에 하드웨어 인터럽트(Interrupt) 감시를 위임한다. USB 포트에 버퍼 바이트가 출현하면 커널이 readyRead() 라는 시그널(Signal)을 빵 쏘아 올린다.

// QGroundControl 측의 비동기 시그널/슬롯 바인딩 (C++)
// 생성자 클래스에서 이벤트와 함수(핸들러)를 묶어둔다 (Connect)
connect(_baseSerialPort, &QSerialPort::readyRead, this, &RTKSettings::readFromBase);

// --- 인터럽트 발생 시에만 즉각적으로 스케줄러가 호출하는 콜백 함수 ---
void RTKSettings::readFromBase() {
    // 1. 쌓여있는 버퍼를 논블로킹(Non-blocking)으로 즉시 털어냄
    QByteArray rtcmData = _baseSerialPort->readAll();

    // 2. 다른 MAVLink 라우팅 클래스(RTCMMavlink)에게 데이터가 왔음을 시그널로 방송 (Broadcast)
    emit rtcmDataUpdate(rtcmData); 
}

2.1 이벤트 파이프라인의 큐(Queue) 확장 방식

rtcmDataUpdate 시그널이 방송되면 텔레메트리 채널 담당자(RTCMMavlink 객체)가 이를 낚아채어 백그라운드 큐 통(Queue Buffer)인 _rtcmBuffer 뒤쪽 배열에 데이터를 조용히 밀어 넣고 사라진다(Push/Return).
이후 QGC 네트워크 전송 담당 스레드가 자기가 편할 때 큐에서 180\text{ bytes} 크기만큼씩만 꺼내서 조각(Chunk) 단위로 MAVLink 텔레메트리에 송신한다.

2.2 장점: 논블로킹(Non-blocking)을 통한 UI/통신 스레드의 자유성(Free) 보장

CPU가 아무리 혹독하게 텔레메트리를 송수신하고 있더라도, C++ 이벤트 루프(Event Loop)는 특정 스레드 하나를 Lock() 상태로 세워두지 않는다. 스레드가 찰나의 시간 안에 긁어서 큐에 넣고 곧바로 리턴(Return)해 버리기 때문에, 메모리 누수나 가비지 콜렉팅 정지가 없으며 UI 애니메이션이 초당 60\text{프레임}으로 부드럽게 유지된다.
베어메탈 통신이나 수십 대의 스웜(Swarm) 환경에서 무수한 MAVLink 창구를 동시에 폴링(Polling)하더라도 지연시간(Latency) 병목 현상(Bottleneck)이 이론적 최소치로 수렴한다.

2.3 단점: 버퍼 오버런(Buffer Overrun)에 의한 지연 늪(Swamp)

단, 통신 모뎀 대역폭 한계를 넘는 과도한 RTCM 패킷이 쏟아져 텔레메트리가 전송을 거부할 경우, 영리한 비동기 스레딩 방식 때문에 QGC 프로그램 자체는 버벅이지 않더라도 _rtcmBuffer 속에 조각이 하염없이 누적된다.
베이스에서 출발한 보정 데이터가 공중을 날아 드론 FC까지 가는 **지연 속도(Delay Rate)**가 비동기 큐잉(Queueing) 밀림 현상 탓에 수 초(\text{Seconds}) 뒤로 늘어지는 숨겨진 지연(Hidden Latency)의 늪에 빠질 치명적 오류를 안고 있다. 때문에 QGC는 큐 최대 상한 용량을 관리해야 하는 고급 휴리스틱(Heuristics)을 필연적으로 품고 있다.

결론적으로, Mission Planner의 C# 기반 블로킹 스레딩 모델은 거칠고 정직하게 데이터 채널을 장악(Takeover)하는 직진(Straight) 철학을, QGC의 C++ Qt 기반 비동기 시그널-슬롯 파이프라인은 복잡한 큐 버퍼링(Queue Buffering)을 통한 전방위 비간섭적(Non-intrusive) 유연성(Flexibility)을 추구하며 드론 관제 아키텍처의 우열 양극단을 증명하고 있다.