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分支)