跳到主要内容

NTRIP差分数据通讯模块

📋 模块概述

NTRIP (Networked Transport of RTCM via Internet Protocol) 差分数据通讯模块提供RTK高精度定位的差分数据接收功能。通过连接NTRIP服务器,实时获取RTCM差分数据并通过串口注入到RTK模块,实现厘米级定位精度。

核心类:

  • android.znhaas.util.NtripManager (NTRIP管理器)
  • android.znhaas.util.NtripClient (NTRIP客户端)
  • android.znhaas.util.RtcmManager (RTCM数据管理器)

协议标准:

  • NTRIP 1.0/2.0: 基于HTTP的差分数据传输协议
  • RTCM 3.x: 国际差分数据格式标准

🎯 主要功能

  • ✅ NTRIP服务器连接
  • ✅ RTCM差分数据接收
  • ✅ GGA位置数据上报
  • ✅ 自动重连机制(指数退避策略)
  • ✅ 网络状态监听
  • ✅ 连接超时检测
  • ✅ 数据统计

📚 核心流程

1. NTRIP工作流程

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ 设备GPS │────▶│ 生成GGA数据 │────▶│ NTRIP服务器 │
│ (粗略位置) │ │ (NMEA格式) │ │ (认证/挂载点)│
└──────────────┘ └──────────────┘ └──────────────┘


┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ RTK模块 │◀────│ 串口注入 │◀────│ RTCM差分数据 │
│ (厘米级定位) │ │ (Hex格式) │ │ (二进制) │
└──────────────┘ └──────────────┘ └──────────────┘

2. 连接状态机

      初始化(Init)


正在连接(Connecting)

├──────────┬──────────┐
▼ ▼ ▼
已连接(Connected) 连接失败 配置错误
│ │
│ ▼
│ 自动重连(指数退避)
│ │
▼ │
断开连接◀────────┘

3. 数据流向

NTRIP服务器 → TCP Socket → NtripClient 

RTCM数据(二进制)

RtcmManager处理

转换为Hex字符串

SerialPort串口注入

RTK模块(差分定位)

📚 核心API

1. 初始化NTRIP管理器

// 获取单例实例
val ntripManager = NtripManager.getInstance(context)

// 监听连接状态
lifecycleScope.launch {
ntripManager.statusFlow.collect { status ->
when (status) {
NtripManager.NtripStatus.Init -> {
// 初始化
}
NtripManager.NtripStatus.Connecting -> {
// 正在连接
Log.d(TAG, "正在连接NTRIP服务器...")
}
NtripManager.NtripStatus.Connected -> {
// 已连接
Log.d(TAG, "NTRIP服务器已连接")
}
NtripManager.NtripStatus.ConnectionFailed -> {
// 连接失败(自动重连)
Log.e(TAG, "NTRIP连接失败")
}
NtripManager.NtripStatus.Disconnected -> {
// 已断开
Log.d(TAG, "NTRIP已断开")
}
NtripManager.NtripStatus.ConfigError -> {
// 配置错误
Log.e(TAG, "NTRIP配置错误")
}
NtripManager.NtripStatus.NetworkLost -> {
// 网络丢失
Log.w(TAG, "网络连接丢失")
}
}
}
}

// 监听数据统计
lifecycleScope.launch {
ntripManager.statsFlow.collect { stats ->
Log.d(TAG, "RTCM数据: 已接收${stats.totalDataReceived}字节, " +
"已注入${stats.totalDataInjected}次")
}
}

2. 配置NTRIP服务器

// 创建配置
val config = NtripManager.NtripConfig(
serverHost = "ntrip.example.com", // 服务器地址
serverPort = 9008, // 端口(默认9008)
mountPoint = "RTCM32", // 挂载点
username = "your_username", // 用户名
password = "your_password", // 密码
reconnectEnabled = true // 启用自动重连
)

// 保存配置
ntripManager.saveConfig(config)

// 获取当前配置
val currentConfig = ntripManager.getConfig()

3. 连接服务器

// 连接到NTRIP服务器
ntripManager.connect()

// 注意: 如果配置不完整,会返回ConfigError状态

4. 发送GGA数据

// 获取GPS位置
val location = locationService.getLastLocation()

// 生成GGA数据
val gga = generateGGA(
latitude = location.latitude,
longitude = location.longitude,
altitude = location.altitude,
time = System.currentTimeMillis()
)

// 发送到NTRIP服务器
ntripManager.sendGGA(gga)

// 建议: 每5秒发送一次GGA数据

GGA数据格式示例:

$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47

字段说明:

  • 123519: UTC时间(时分秒)
  • 4807.038,N: 纬度(度分格式,北纬)
  • 01131.000,E: 经度(度分格式,东经)
  • 1: GPS定位质量(0=无效,1=GPS,2=差分GPS)
  • 08: 使用的卫星数量
  • 0.9: 水平精度因子(HDOP)
  • 545.4,M: 海拔高度(米)

5. 断开连接

// 手动断开连接
ntripManager.disconnect()

// 注意: 手动断开不会触发自动重连

6. 自动重连控制

// 启用自动重连
ntripManager.setReconnectEnabled(true)

// 禁用自动重连
ntripManager.setReconnectEnabled(false)

// 重连策略:
// - 指数退避: 3s, 6s, 12s, 24s, 48s
// - 最大延迟: 60秒
// - 最大尝试: 5次
// - 网络恢复后自动重连

📚 NtripClient (底层客户端)

核心功能

// 创建客户端
val ntripClient = NtripClient(context)

// 配置服务器
ntripClient.configure(
serverHost = "ntrip.example.com",
serverPort = 9008,
mountPoint = "RTCM32",
username = "user",
password = "pass"
)

// 设置RTCM数据监听器
ntripClient.setRtcmDataListener(object : NtripClient.RtcmDataListener {
override fun onRtcmDataReceived(data: ByteArray) {
// 处理接收到的RTCM数据
val hexString = data.joinToString("") { String.format("%02X", it) }
Log.d(TAG, "接收RTCM: $hexString")

// 注入到串口
val command = SerialPortProtocol.createRTCMBusinessData(hexString)
SerialPort().sendHex(command)
}
})

// 设置连接状态监听器
ntripClient.setConnectionStateListener(object : NtripClient.ConnectionStateListener {
override fun onConnecting() {
Log.d(TAG, "正在连接...")
}

override fun onConnected() {
Log.d(TAG, "连接成功")
}

override fun onConnectionFailed(reason: String) {
Log.e(TAG, "连接失败: $reason")
}

override fun onDisconnected() {
Log.d(TAG, "已断开连接")
}
})

// 连接服务器
ntripClient.connect()

// 发送GGA数据
ntripClient.sendGGA(ggaString)

// 断开连接
ntripClient.disconnect(isManualDisconnect = true)

📚 完整使用示例

示例: RTK差分定位完整流程

class RtkLocationActivity : AppCompatActivity() {
private val TAG = "RtkLocation"

private lateinit var ntripManager: NtripManager
private lateinit var locationService: NativeLocationService
private var ggaTimer: Timer? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 1. 初始化定位服务
locationService = NativeLocationService.getInstance(this)
locationService.startLocationUpdates()

// 2. 初始化NTRIP管理器
ntripManager = NtripManager.getInstance(this)

// 3. 监听连接状态
lifecycleScope.launch {
ntripManager.statusFlow.collect { status ->
updateUI(status)

// 连接成功后启动GGA定时发送
if (status == NtripManager.NtripStatus.Connected) {
startGgaTimer()
} else {
stopGgaTimer()
}
}
}

// 4. 监听数据统计
lifecycleScope.launch {
ntripManager.statsFlow.collect { stats ->
Log.d(TAG, "RTCM统计: 接收${stats.totalDataReceived}字节, " +
"注入${stats.totalDataInjected}次")
}
}

// 5. 配置NTRIP服务器
val config = NtripManager.NtripConfig(
serverHost = "rtk.example.com",
serverPort = 9008,
mountPoint = "RTCM32_GG",
username = "user",
password = "pass",
reconnectEnabled = true
)
ntripManager.saveConfig(config)

// 6. 连接服务器
ntripManager.connect()
}

/**
* 启动GGA定时发送(每5秒)
*/
private fun startGgaTimer() {
stopGgaTimer()

ggaTimer = Timer().apply {
scheduleAtFixedRate(object : TimerTask() {
override fun run() {
sendGgaData()
}
}, 0, 5000L) // 每5秒发送一次
}

Log.d(TAG, "GGA定时器已启动")
}

/**
* 停止GGA定时发送
*/
private fun stopGgaTimer() {
ggaTimer?.cancel()
ggaTimer = null
}

/**
* 发送GGA数据
*/
private fun sendGgaData() {
// 获取当前位置
val location = locationService.getLastLocation()

if (location.latitude == 0.0 || location.longitude == 0.0) {
Log.w(TAG, "位置无效,跳过GGA发送")
return
}

// 生成GGA字符串
val gga = NmeaUtil.generateGGA(
latitude = location.latitude,
longitude = location.longitude,
altitude = location.altitude,
quality = 1, // GPS定位
satellites = 8,
hdop = 1.0f
)

// 发送到NTRIP服务器
ntripManager.sendGGA(gga)

Log.d(TAG, "发送GGA: $gga")
}

/**
* 更新UI
*/
private fun updateUI(status: NtripManager.NtripStatus) {
runOnUiThread {
when (status) {
NtripManager.NtripStatus.Connected -> {
statusText.text = "✅ RTK已连接"
statusText.setTextColor(Color.GREEN)
}
NtripManager.NtripStatus.Connecting -> {
statusText.text = "🔄 RTK连接中..."
statusText.setTextColor(Color.YELLOW)
}
NtripManager.NtripStatus.Disconnected -> {
statusText.text = "❌ RTK未连接"
statusText.setTextColor(Color.GRAY)
}
NtripManager.NtripStatus.ConnectionFailed -> {
statusText.text = "⚠️ RTK连接失败"
statusText.setTextColor(Color.RED)
}
else -> {}
}
}
}

override fun onDestroy() {
super.onDestroy()

// 停止GGA定时器
stopGgaTimer()

// 断开NTRIP连接
ntripManager.disconnect()

// 停止定位服务
locationService.stopLocationUpdates()
}
}

🔧 注意事项

1. NTRIP服务器要求

必需参数:

  • ✅ 服务器地址(serverHost)
  • ✅ 挂载点(mountPoint)

可选参数:

  • 端口号(默认9008)
  • 用户名/密码(公共服务器可能不需要)

2. GGA数据发送频率

推荐频率: 5秒/次

注意事项:

  • 发送过快会增加服务器负载
  • 发送过慢会影响差分数据的时效性
  • 某些服务器要求必须发送GGA才提供数据

3. 网络要求

  • ✅ 稳定的网络连接(WiFi/4G/5G)
  • ✅ 最低带宽: 10KB/s
  • ✅ 延迟: 建议小于500ms

4. 重连机制

指数退避策略:

1次: 3秒后重连
2次: 6秒后重连
3次: 12秒后重连
4次: 24秒后重连
5次: 48秒后重连(最多5次)

网络恢复自动重连:

  • 检测到网络恢复后自动尝试重连
  • 重置重连计数器

5. 连接超时

  • ✅ 读取超时: 15秒
  • ✅ 数据超时: 120秒(无数据接收)
  • ✅ 超时后自动断开并触发重连

6. 权限要求

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

7. 线程安全

  • ✅ 所有网络操作在IO线程
  • ✅ 使用StateFlow进行状态管理
  • ✅ 使用AtomicBoolean/AtomicLong保证线程安全

📖 相关资源

源码位置

  • NtripManager: app/src/main/java/android/znhaas/util/NtripManager.kt
  • NtripClient: app/src/main/java/android/znhaas/util/NtripClient.kt
  • RtcmManager: app/src/main/java/android/znhaas/util/RtcmManager.kt

协议标准

公共NTRIP服务器

扩展阅读


最后更新: 2025-01-19
文档版本: v1.0
基于代码版本: H07 Android App (main分支)