-
[MATLAB] MATLAB TCP 클라이언트 연결과 데이터 스트림항법연구실/MATLAB 2026. 7. 16. 21:09
GPS실시간 데이터 처리에 사용하는 Onboard, Sequential개념을 이해하지 못해 Ntrip Client를 매트랩에서 사용하고 구현하는 과정을 통해 이해해보려고 한다.
전체적인 연구 주제는
MATALB 환경에서 Ntrip Client에 연결하여 실시간 데이터를 받고 지상국의 위치 결정까지 실시간으로 진행하기
그리고 세부 주제
1. MATLAB에서 Ntrip Client연결
2. Stream 데이터(Binary) Decode
3. Pesudorange, Ephemeris Data 받아오기(RTCM3 message Type활용)
4. 위치 결정(Least Square)
그럼 나의 하찮지만 귀여운 연구노트 ... 를 공개합니다 ..
이번 편에서는 MATLAB에서 TCP클라이언트에 연결해서 어떻게 Stream 데이터를 받아오는지까지 작성할 예정이다.


활용한 Opensource
https://github.com/msbillah/NtripClient-MATLAB
GitHub - msbillah/NtripClient-MATLAB: A standalone ntrip-client written for MATLAB. Original code by GoGPS.
A standalone ntrip-client written for MATLAB. Original code by GoGPS. - msbillah/NtripClient-MATLAB
github.com
goGPS를 활용해서 간략화한 NtripClient-MATLAB이라는 오픈소스를 활용하여 코드를 작성하였다.
- RTCM3 버전만 사용
- Message Type모두 반환하도록 변경 (decode 코드는 추가 X)
- 기존 tcpip(Legacy) → tcpclient로 변경
- 출력 데이터 format 변경(우리 연구실 Subroutine에서 사용할 수 있는 format으로 ..)
TCP/IP은 사실 Stream(바이트의 연속 흐름)
소프트웨어 학부 때는 데이터를 패킷 단위로 받아서 처리하는 과정을 진행했다. 그런데.. OS(운영체제)소켓 레이어에서는 패킷의 껍데기가 다 풀린 채로 끊김 없이 흐르는 하나의 파이프라인(Stream)으로 변한다.
어떤 느낌이냐면 ....

이렇게 패킷은 예쁘게 하나씩 포장되어 나온다면, Stream은 물밀듯 밀려나온다. 그럼 이제 사용자가 해야할 일이 많아지는 것이다.
1. 데이터를 어떻게 자를까?
2. 데이터를 어떻게 decode할까?
등등 ..
우선 이를 정의하기 위해 Stream에 대해서 알아보자
Stream이란?
인터넷 망을 통과할 때, 데이터는 수많은 패킷으로 쪼개져 오지만, 실제 컴퓨터 OS네트워크 엔진에 도달하는 순간 패킷들의 포장은 모두 벗겨진다. 그리고 Bytes만 순서대로 엮여 파이프라인(Stream)을 형성한다.

연구노트에 휘갈긴 내용이라 조금 지저분하지만 .. - 경계가 없다: 스트림 통신에서는 어디서부터 어디까지가 하나의 패킷인지 알려주는 구분선이 없다.
- FIFO(First-In, First-Out), 큐(Queue)구조:
컴퓨터 메모리에 Socket Receiver Buffer라는 대기 공간이 있다. 서버가 던진 데이터는 순서대로 이 버퍼의 맨 끝단에서 쌓이고 내가 읽을 때는 맨 앞에서부터 차례대로 빠져나간다.
MATLAB에서 Ntrip Client 연결
Ntrip Caster도 TCP/IP 소켓 통신을 사용한다.
tcpclient를 통한 Ntrip Client 연결
master = tcpclient(master_ip, master_port); if (flag_NTRIP) encoded_auth = base64encode(sprintf('%s:%s',ntrip_user,ntrip_pw),''); nmeastring = sprintf('%s\r\n',nmea_init); ntripstring = sprintf('GET /%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: NTRIP SNL\r\nConnection: close\r\nAuthorization: Basic %s\r\n\r\n%s\r\n', ntrip_mountpoint, master_ip, encoded_auth, nmeastring); fprintf('NTRIP request [%s]',ntripstring); write(master, uint8(ntripstring)); endNtrip Client에 내 정보(ntripstring)을 함께 보내면 Ntrip Caster(서버)에서 확인해준다. 그 이후 서버가 접속을 끊지 않고 실시간 RTCM3바이너리 스트림을 우리 수신 버퍼로 전송해준다. 다음은 대전 지상국을 기준으로 한 ntripstring예시이다.
GET /DAEJ00KOR0 HTTP/1.1 Host: 123.456.78.90 User-Agent: NTRIP SNL Connection: close Authorization: Basic amVvbmc6MTIzNA== $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47기존 오픈소스에서 사용하던 tcpip를 tcpclient로 변경하였다. 동일한 역할을 하는 함수라 크게 차이는 없었으나, InputBufferSize(버퍼의 최대 용량)을 설정하지 않아도 되고 fopen이 불필요하다는 것 정도의 차이가 있었다.
서버와 연결이 성공하면 이제 버퍼는 계속 밀려들어오는 Stream을 빼주어야한다. 이를 위해 사용하는 핵심 도구가 NumbytesAvailable과 read함수이다.
- BytesAvailable:
현재 내 버퍼에 읽지 않은 데이터가 얼마나 쌓여있는가?
- read함수:
버퍼에서 원하는 크기만큼 데이터를 퍼내라
그럼 이 핵심 도구를 사용해서 데이터를 읽고 쓰는 방식에 대해서 정리해보자.
데이터를 언제까지 읽어야할까?
Stream 데이터를 끊어내는 기준
Ntrip Caster는 1초의 RTCM데이터를 한 번에 통째로 주지 않는다. 인터넷 망 사정으로 인해 잘게 쪼개져들어온다. 그런데 데이터가 들어오는 중간에 read를 해버리면 데이터 뒷부분이 잘려나가 디코딩 에러가 발생한다.
%TCP/IP port checking master_1 = get(master,'BytesAvailable'); pause(server_delay); master_2 = get(master,'BytesAvailable'); %test if the package writing is finished if (master_1 == master_2) && (master_1 ~= 0) data_master = read(master,master_1,'uint8'); %TCP/IP port reading ... endBytesAvailable을 통해서 Stream이 잠깐 멈추는 구간을 찾는다.
pause(0.05)동안 서버가 데이터를 계속 넣고 있다면 master_1보다 master_2가 더 커진다. 반면, 0.05초가 지나도 수신 버퍼의 데이터 양에 변화가 없다면 이번 Epoch에 보낼 패킷들이 내 컴퓨터 버퍼에 완전히 다 쌓여있다는 뜻이다.
- master_1 < master_2: 데이터가 계속 전송되는 중
- master_1 == master_2: 데이터 유입이 완전히 끝남
master_1 == master_2일때, 데이터를 꺼내오면 되는 것이다.
read함수는 데이터를 꺼내고 버퍼를 비우는 역할을 한다.
읽어온 데이터는 data_master에 저장한다.
read를 통해 읽어온 Stream 데이터
RAW 데이터를 디코더가 읽을 수 있는 형태로 가공
그럼 이제 data_master를 통해 Stream데이터를 엿볼 수 있다. 그리고 RAW데이터를 Decoder가 읽을 수 있도록 가공하는 흐름을 살펴보자
① TCP/IP Port reading
data_master = 1x346 uint8 row vector Columns 1 through 31 211 0 196 62 192 0 91 244 181 226 192 68 218 96 237 251 38 159 210 110 192 22 63 59 251 253 194 105 53 182 207 Columns 32 through 62 222 17 254 153 71 255 153 249 162 191 219 21 31 124 165 253 157 151 244 171 0 4 223 164 237 255 110 57 115 46 231 Columns 63 through 93 253 34 127 167 210 128 29 255 144 23 249 145 91 70 239 95 194 23 233 82 104 1 251 246 249 124 77 8 96 65 213 ...MATLAB의 read함수는 끊임없이 흐르는 Stream신호를 8비트(1바이트)단위로 끊어서 가져온다. 이 때 MATLAB은 uint8자료형으로 8비트 이진수를 번역하여 보낸다.
② Conversion to binary (N x 8bit)
data_master = dec2bin(data_master,8)'; %conversion to binarydata_master = 8x346 char array '1010100110101111101010000110100111101011111011111110101010010110111001101101001010000101000011011100111111101100010111011001110100100011000110010100001010110011000000100101101010010100111100111111011001101000001000101010101011110100101111111110100010101110110110101010110000001100000011010000000010000110110011001011001101110101101110100000000000' '1010101111110011011110000111100101011010010000100101100011000011001001011010101111100110101111101110000101111101100010110010111100110010001010010111101010000110100010101001111100110111100110010011110100100010100000001011110001110011101010011110100000110010011011010011010000001000100111100000001000001100110111000001010000110100101101100000000010' '0001000111001001010100110101010011001110100001110110111111100111110011101101110010101101001111011010001111100111010011011010111110100010010010001001111101100110101000101001111110100110000111100011110100000100111000010111110001011010000111010101000101101101111110000010011001111110110101010000001011101101110110010101011111110110101100110010000011' '1001001100001001011000100110001111111011111110100101100110111010101011101110011111101101000101010101110110101111000110010010100100100011101010110001011001011110101001100011101000010111011101000111110000100100001000000111110010110011101010010111001100111011110010110011101001001100100111000000001110001101110011110101110111111100111111000000000101' '0001001000001101010101110110101011101010110110100010110010101110100011001010110000001111011001111000110100100011110111101100111110110011110000101001011101101110101011010011110101111111010001110110110111001100001000001101110011011101111010111001001100001001011110100010110001011101000011100100000001010111110011111001010011101001011101111000000111' '0011000100010010110101101101111111011001111010101100111110001111110001110001111100000001100001010101100100011101100111110011101011100111011110110001111010000011001001000001011000110100001001000110100010000100000101100111000011111010111110111011111101101001010100001011100011101010110110000000000100001100100011100000111011111010110101000000000101' '1001001111001001111101101011101001001100100010001011110111001110111001010001110101011000101110010001100101111101000110101101111111101010000110100001110010001011100101101011010100100100011111000100110010101100000010101101110111010010011111011111100111011011100100000100100001010000000011110000000110000110100111100011111111111111100101010000000101' '1000001100001110110000101101101100111101101011101011111110101100110011111101111110001111001101011000111010111011010101101000111001101010001101100010111101101101000101110000110101110100111011100011100010100100111010010111000110010100011000011011100111101011101101000111110001111001110010100000000010010010001010000011011010111100101111110000000111'MATLAB이 만들어준 포장지를 뜯어 다시 이진수 문자열로 강제 변환한다. 10진수 정수형태는 비트 단위의 파싱이 어렵기 때문이다.
③ Conversion to 8N bit vector
data_master = data_master (:); %transpose (8bit x N matrix) data_master = data_master'; %conversion to string (8N bit vector)data_master = '10101001101011111010100001101001111010111110111111101010100101101110011011010010100001010 000110111001111111011000101110110011101001000110001100101000010101100110000001001011010100 101001111001111110110011010000010001010101010111101001011111111101000101011101101101010101 100000011000000110100000000100001101100110010110011011101011011101000000000001010101111110 011011110000111100101011010010000100101100011000011001001011010101111100110101111101110000 101111101100010110010111100110010001010010111101010000110100010101001111100110111100110010 011110100100010100000001011110001110011101010011110100000110010011011010011010000001000100 111100000001000001100110111000001010000110100101101100000000010000100011100100101010011010 101001100111010000111011011111110011111001110110111001010110100111101101000111110011101001 101101011111010001001001000100111110110011010100010100111111010011000011110001111010000010 011100001011111000101101000011101010100010110110111111000001001100111111011010101000000101 110110111011001010101111111011010110011001000001110010011000010010110001001100011111110111 111101001011001101110101010111011100111111011010001010101011101101011110001100100101001001 000111010101100010110010111101010011000111010000101110111010001111100001001000010000001111 100101100111010100101110011001110111100101100111010010011001001110000000011100011011100111 101011101111111001111110000000001010001001000001101010101110110101011101010110110100010110 010101110100011001010110000001111011001111000110100100011110111101100111110110011110000101 001011101101110101011010011110101111111010001110110110111001100001000001101110011011101111 010111001001100001001011110100010110001011101000011100100000001010111110011111001010011101 001011101111000000111001100010001001011010110110111111101100111101010110011111000111111000 111000111110000000110000101010110010001110110011111001110101110011101111011000111101000001 100100100000101100011010000100100011010001000010000010110011100001111101011111011101111110 110100101010000101110001110101011011000000000010000110010001110000011101111101011010100000 000010100100111100100111110110101110100100110010001000101111011100111011100101000111010101 100010111001000110010111110100011010110111111110101000011010000111001000101110010110101101 010010010001111100010011001010110000001010110111011101001001111101111110011101101110010000 010010000101000000001111000000011000011010011110001111111111111110010101000000010100000110 000111011000010110110110011110110101110101111111010110011001111110111111000111100110101100 011101011101101010110100011100110101000110110001011110110110100010111000011010111010011101 110001110001010010011101001011100011001010001100001101110011110101110110100011111000111100 111001010000000001001001000101000001101101011110010111111000000011'마지막으로 행렬 전치 후, 일렬로 만들면 Decoder에 들어갈 1차원 이진 문자열 벡터(Bit Stream)가 완성된다.
감사하게도 NtripClient에서 이 모든 과정을 알려주셨다.
Decode를 하는 과정은 RTCM3과 관계가 있어서 다음 RTCM3를 다루는 포스팅에서 같이 작성해보겠다.
오픈소스 짱 !
'항법연구실 > MATLAB' 카테고리의 다른 글
[MATLAB] RS-232통신을 Linux, MATLAB 환경에서 구현하기 (0) 2026.06.25 [MATLAB] serialport사용하기 (0) 2026.06.23