自建NTRIP服务器

告别 CORS 年费 · 零丢包 RTCM 转发链路

NTRIP RTCM C# Python
GNSS基站 串口 C#上位机 基站端 TCP RTCM 腾讯云 Python Relay NTRIP Server TCP C#上位机 移动站端 串口 移动站
↓ 向下滑动阅读

📡 背景

每次测试都要购买CORS差分账号,一年下来是一笔不小的开销。而且CORS账号有地域限制、有效期限制,用起来很麻烦。

为什么不用 CORS?

每次测试都要购买CORS差分账号,一年下来是一笔不小的开销。而且CORS账号有地域限制、有效期限制,用起来很麻烦。后来想了个办法:自己搭一个NTRIP服务器,把基站RTCM数据传到云上,移动站从云上拉。

于是有了这套系统:

data-chain.txt
GNSS基站 → 串口 → C#上位机 → TCP → 腾讯云 → TCP → C#上位机 → 串口 → 移动站

🛠 架构总览

完整的数据链路:从基站采集到移动站接收,全链路自建。

基站 COM 串口 C# 上位机 TCP 腾讯云 C# 上位机 COM 串口 移动站 RS232 Serial TCP/IP TCP/IP Serial
✅ 全链路自建
不依赖任何第三方CORS服务,基站RTCM 3.x数据通过自建Python Relay在腾讯云中转,移动站实时拉取。整条链路延迟极低,数据完整性有保障。

🐍 Python RTCM Relay

用 Python asyncio 写了一个轻量 TCP relay,支持多基站、多移动站。不到200行代码。

rtcm_relay.py — 核心逻辑
# rtcm_relay.py (核心逻辑)
class RTCMRelay:
    def __init__(self):
        self.base_clients = set()   # 基站连接
        self.rover_clients = set()  # 移动站连接

    async def handle_base(self, reader, writer):
        # 基站发来的数据广播给所有移动站
        data = await reader.read(4096)
        for rover in self.rover_clients:
            rover.write(data)
            await rover.drain()
~200
行代码
asyncio
异步框架
TCP
传输协议

🖥 C# WPF 上位机

从零开始用 C# .NET 8 写了 WPF 客户端,支持基站模式、移动站模式、测试模式。

三种工作模式

基站模式 — 连接GNSS接收机串口,读取RTCM数据,通过TCP上传到云端Relay服务器。

移动站模式 — 从云端Relay拉取RTCM差分数据,通过串口写入移动站接收机。

测试模式 — 自发自收,验证整条TCP转发链路的完整性与丢包率。

⚠️ 踩过的坑

  • Dispatcher.Invoke 死锁 → 改用 BeginInvoke
  • SerialPort.GetPortNames 卡死 → 放后台线程+超时
  • XAML 绑定只读属性异常 → 改为 INotifyPropertyChanged
  • 关闭时假死 → 关闭操作扔后台+3秒超时

🔒 安全加固

最初端口裸奔,后来加了三层防护。

安全策略

  • 令牌认证 — 连接后先发密钥,否则秒断
  • 速率限制 — 单连接超100KB/s自动断开
  • 单基站模式 — 新连接自动踢旧连接

💥 翻车现场

把自己封了 — 一个关于IP归属地的惨痛教训。

🔥 封了自己的外网IP

日志里看到一个陌生IP在连5001端口,查归属地"上海移动"。我说"封了它" — iptables DROP。然后整个服务器断联了,SSH也上不去。

最后发现:那是我自己的外网IP(长沙移动宽带)。IP归属地数据库把长沙标记成了上海。从Web Console登录解封后,我和AI同事笑了一整晚。

从今以后:封IP前必须先让用户查自己外网IP。

🏆 成果

系统已经稳定运行7天+,RTCM 3.x 差分数据零丢包转发。

0
天+ 稳定运行
0%
丢包率
102.5KB
测试模式验证
~200
行 Relay 代码

测试模式验证

测试模式验证了转发链路的完整性:发送102.5KB,接收102.5KB,丢包率0%。RTCM 3.x差分数据在整条链路上实现了零损耗传输。

— 高昆,2026年6月,于长沙

下一篇 →
← 返回