跳转至

DraARLv1 协议规范

协议概述

DraARLv1 (Digital Radio Advanced Application Protocol v1) 是 DraARL 平台的设备通信协议,用于设备与服务器之间的实时通信。

报文结构

整体布局

+--------+--------+--------+--------+--------+--------+--------+--------+
| 0-3    | 4-5    | 6-37   | 38-47  | 48     | 49     | 50     | 51-53  |
| Ver    | Length |Username|DevPass | Type   |DevModel| SSID   | DMRID  |
| 4B     | 2B     | 32B    | 10B    | 1B     | 1B     | 1B     | 3B     |
+--------+--------+--------+--------+--------+--------+--------+--------+
| 54-85  | 86-89  | 90+                                              |
|CallSign| Rsv    | DATA                                             |
| 32B    | 4B     | 变长                                             |
+--------+--------+--------+-------------------------------------------------+

固定头部:90 字节
最小报文:90 字节
最大报文:800 字节(UDP 服务端限制,超出将被静默丢弃)

字节序

所有多字节字段使用 大端序 (Big-Endian)


字段详解

偏移 长度 字段名 类型 说明
0 4B Version string 协议版本标识,固定为 "DraA" (0x44726141)
4 2B Length uint16 BE 报文总长度(包含头部和数据)
6 32B Username string 用户名,UTF-8 编码,不足部分用 \0 填充
38 10B DevicePassword string 设备准入密码,ASCII 字母数字,不足部分用 \0 填充
48 1B Type byte 数据包类型,见 数据包类型
49 1B DevModel byte 设备型号,见 设备型号
50 1B SSID byte 设备子号 (0-255)
51 3B DMRID uint24 BE DMR ID
54 32B CallSign string 业余电台呼号,服务器填充,设备发送时留空
86 4B Reserved - 保留字段,填 0
90 变长 DATA []byte 负载数据

数据包类型

常量名 说明 DATA 内容
0 TypeControl 控制指令 控制命令数据
1 TypeJWTAuth JWT 认证 JWT Token 字符串(见 JWT 认证包格式
2 TypeHeartbeat 心跳包 可选携带 GPS 位置信息
3 TypeConfig 设备配置 TLV 格式配置数据(见 Config 包协议
4 TypeTextMessage 文本消息 UTF-8 编码文本
5 TypeOpus16K Opus 16K 语音 Opus 16kHz 编码语音帧
6 TypeServerVoice 服务器互联语音 服务器互联语音格式
7 TypeATPassThrough AT 透传 AT 命令透传

设备型号

常量名 说明 认证方式
0 DevModelUnknown 未知设备 -
100 DevModelWeChatMini 微信小程序 -
101 DevModelAndroid Android 客户端 JWT Token
102 DevModelIOS iOS 客户端 JWT Token
103 DevModelWindows Windows 客户端 JWT Token
104 DevModelMacOS macOS 客户端 (预留) JWT Token
105 DevModelBrowser 浏览器客户端 JWT Token
106 DevModelInterconnect 互联设备 设备密码
107 DevModelESP32 ESP32 链路台/手咪 设备密码

SSID 分配规则

SSID 范围 用途 认证方式 说明
1-99 普通设备 设备密码 用户自定义,适用于嵌入式设备
100 预留 - 原微信小程序
101-104 UDP 幽灵设备 JWT Token App/PC 客户端,SSID = DevModel
105 Web 幽灵设备 JWT Token WebSocket 客户端
106-235 普通设备扩展 设备密码 用户自定义,适用于嵌入式设备
236-255 服务器互联保留 - 系统保留,用户不可分配

普通设备可用范围: 1-99 和 106-235 (共 229 个),使用设备密码认证 幽灵设备范围: 101-105,使用 JWT Token 认证,SSID 固定等于 DevModel


Config 包协议

TypeConfig (Type=3) 仅用于 UDP 普通设备(SSID 1-99, 106-235)的配置同步,不适用于幽灵设备(App/PC 客户端)。

包类型

DATA[0] 方向 说明
0x01 服务端 → 设备 查询配置请求
0x02 双向 配置下发/上报(支持动态下发部分配置项)
0x03 服务端 → 设备 时间同步

TLV 数据格式

当 DATA[0] = 0x02 时,DATA 区域格式:

DATA[0] = 0x02 (配置标识)
DATA[1] = N (配置项数量)
DATA[2...] = TLV 列表

TLV 格式:
┌──────────┬──────────┬─────────────────┐
│ Type (1B)│ Len (1B) │ Value (N bytes) │
└──────────┴──────────┴─────────────────┘

配置项 Type 定义

Type 配置键 长度 格式 说明
0x01 rx_freq 8 big-endian uint64 接收频率 (Hz)
0x02 tx_freq 8 big-endian uint64 发射频率 (Hz)
0x03 rx_ctcss 4 big-endian float32 接收亚音 (Hz, 0=关闭)
0x04 tx_ctcss 4 big-endian float32 发射亚音 (Hz, 0=关闭)
0x05 sql_level 1 uint8 静噪等级 (0-9)
0x06 power_level 1 uint8 功率等级 (1=低, 2=中, 3=高)
0x07 tx_bandwidth 1 uint8 发射带宽 (1=窄带, 2=宽带)
0x10 timestamp 8 big-endian int64 Unix 时间戳 (毫秒)

示例

下发单个配置项(接收频率 439.500MHz)

DATA = [0x02, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x15, 0x7C, 0x00]
         │     │     │     │     └─────────────────────────────────────────────┘
         │     │     │     │                       439500000 Hz (0x1A157C00)
         │     │     │     └─ 长度 8
         │     │     └─ Type=rx_freq
         │     └─ 配置项数量 1
         └─ 配置下发标识

设备上报全部配置

DATA = [0x02, 0x07, TLV1, TLV2, TLV3, TLV4, TLV5, TLV6, TLV7]
              └─ 全部 7 个配置项

同步流程

设备上线同步

设备上线 (认证成功)
    │
    ├─ 数据库有配置记录
    │   └─ 服务端发送 Config(0x02 + TLV) 下发配置
    │
    └─ 数据库无配置记录
        └─ 服务端发送 Config(0x01) 查询请求
            │
            └─ 设备上报 Config(0x02 + TLV)
                │
                └─ 服务端解析存储到数据库

控制台修改同步

用户通过控制台修改配置
    │
    ├─ 保存变更到数据库
    │
    └─ 设备在线?
        ├─ 是 → 发送 Config(0x02 + 变更项TLV) 动态下发
        └─ 否 → 等待设备上线时同步

时间同步

服务端定期或设备上线时发送 Config(0x03 + 8字节时间戳),设备同步系统时间。


特殊数据包格式

JWT 认证包格式 (TypeJWTAuth)

当 Type = 1 (TypeJWTAuth) 时,用于幽灵设备(App/PC 客户端)的 JWT Token 认证。

认证请求

客户端发送 JWT Token 进行身份认证:

┌──────────────────────────────────────────────────────────────┐
│  Header (90 字节)                                            │
│    Version: "DraA"                                           │
│    Length: 90 + Token长度                                    │
│    Username: 空 (可选,用于日志)                              │
│    DevicePassword: 空                                        │
│    Type: 1 (TypeJWTAuth)                                     │
│    DevModel: 101/102/103/104                                 │
│    SSID: 0 (服务器会用 DevModel 作为 SSID)                    │
│    DMRID: 0                                                  │
│    CallSign: 空 (服务器填充)                                  │
│    Reserved: 4字节保留                                        │
├─────────────────────────────────────────────────────────—────┤
│  DATA 区域                                                    │
│    JWT Token 字符串 (UTF-8 编码,直到包尾)                    │
└──────────────────────────────────────────────────────────────┘

认证响应

服务器返回认证结果:

┌──────────────────────────────────────────────────────────────┐
│  Header (90 字节)                                            │
│    Version: "DraA"                                           │
│    Length: 90 + DATA长度                                     │
│    Username: 回显用户名                                       │
│    DevicePassword: 空                                        │
│    Type: 1                                                   │
│    DevModel: 回显                                            │
│    SSID: 服务器分配 (等于 DevModel)                           │
│    DMRID: 0                                                  │
│    CallSign: 成功时填充用户呼号,失败时为空                    │
│    Reserved: 4字节保留                                        │
├──────────────────────────────────────────────────────────────┤
│  DATA 区域                                                    │
│    [0]: 状态码                                                │
│         0 = 认证成功                                          │
│         1 = Token 无效或过期                                  │
│         2 = 用户不存在                                        │
│         3 = 用户已禁用                                        │
│         4 = 用户未审核                                        │
│         5 = 无效的设备型号 (非 101-104)                       │
│    [1:]: 成功时为空,失败时为错误消息文本                      │
└──────────────────────────────────────────────────────────────┘

认证流程

┌─────────┐                              ┌─────────┐
│ App/PC  │                              │  服务器  │
└────┬────┘                              └────┬────┘
     │                                        │
     │  JWT Auth (Type=1)                     │
     │  DevModel: 101/102/103/104             │
     │  DATA: <JWT Token>                     │
     │───────────────────────────────────────>│
     │                                        │
     │                                        │ 1. 验证 JWT Token
     │                                        │ 2. 获取用户信息
     │                                        │ 3. 检查用户状态
     │                                        │ 4. 注册幽灵设备
     │                                        │
     │  JWT Auth Response                     │
     │  SSID: 101/102/103/104                 │
     │  CallSign: "BG1AAA"                    │
     │  DATA[0]: 0 (成功)                     │
     │<───────────────────────────────────────│
     │                                        │

注意:JWT 认证包仅用于幽灵设备(DevModel 101-104)。普通设备(ESP32 等)仍使用设备密码认证,通过心跳包完成。

Opus 语音格式 (TypeOpus16K)

当 Type = 5 (TypeOpus16K) 时,DATA 区域携带 Opus 编码的语音数据。

编码参数

参数 说明
采样率 16000 Hz 16kHz 宽带语音
声道数 1 单声道
帧时长 60 ms 每个 Opus 帧 60ms
每帧采样数 960 16000 × 0.06
比特率 16-32 kbps VOIP 模式

合并帧格式(WebSocket 客户端)

为优化弱网性能,WebSocket 客户端采用 2 帧合并发送 策略: - 每 120ms 发送一个数据包 - 每个数据包包含 2 个 60ms 的 Opus 帧 - 使用帧长度前缀标识每帧边界

DATA 区域格式:

+-------------+-------------+-------------+-------------+
| Frame1 Len  | Frame1 Data | Frame2 Len  | Frame2 Data |
| 2B          | N bytes     | 2B          | M bytes     |
+-------------+-------------+-------------+-------------+
字段 长度 说明
Frame1 Len 2B 第 1 帧长度(大端序 uint16)
Frame1 Data 变长 第 1 帧 Opus 数据(60ms)
Frame2 Len 2B 第 2 帧长度(大端序 uint16)
Frame2 Data 变长 第 2 帧 Opus 数据(60ms)

性能优化说明

项目 优化前 优化后
帧时长 20ms 60ms
发送间隔 20ms 120ms
每秒发包数 50 ~8.3
单包大小 110-170B 330-570B
头部开销占比 50-80% ~25-35%
额外延迟 - +100ms

注意:120ms 延迟对半双工语音通信完全可接受,且大幅降低弱网丢包影响。

解码兼容性

接收端应支持: 1. 合并帧格式:按长度前缀解析多个 Opus 帧 2. 单帧格式:兼容无长度前缀的单帧数据(用于 UDP 设备直发)

判断逻辑: - 若首字节值 < 0x80,视为合并帧格式(长度前缀) - 否则视为单帧格式

服务器互联语音格式 (TypeServerVoice)

当 Type = 6 (TypeServerVoice) 时,DATA 区域前 68 字节用于存储原始发送方信息:

+--------+--------+--------+--------+
| 0-31   | 32-63  | 64-67  | 68+    |
|OrigUser|OrigCall|OrigIP  | Voice  |
| 32B    | 32B    | 4B     | 变长    |
+--------+--------+--------+--------+
偏移 长度 字段名 说明
0 32B OriginalUsername 原始发送方用户名
32 32B OriginalCallSign 原始发送方呼号
64 4B OriginalIP 原始服务器 IP
68 变长 VoiceData 实际语音数据

心跳包扩展格式

心跳包的 DATA 区域可携带位置信息:

+--------+--------+--------+--------+
| 0-7    | 8-15   | 16-23  | 24+    |
| Lat    | Lon    | Alt    | Extra  |
| 8B     | 8B     | 8B     | 变长    |
+--------+--------+--------+--------+
偏移 长度 字段名 说明
0 8B Latitude 纬度 (float64 BE)
8 8B Longitude 经度 (float64 BE)
16 8B Altitude 海拔高度 (float64 BE)

设备认证流程

认证方式(三轨制)

平台支持三种认证方式,根据设备类型和场景自动选择:

设备类型 SSID 范围 认证方式 说明
普通设备 1-99, 106-235 设备密码 ESP32、射频互联盒子等嵌入式设备
幽灵设备 101-105 JWT Token App/PC 客户端 (Android/iOS/Windows/macOS/Web)
新设备绑定 1-99, 106-235 动态码绑定 新设备首次上线,通过动态码完成账号绑定

设计说明: - 普通设备(如 ESP32 链路台/手咪)性能有限,不适合进行 JWT 解析,继续使用传统的设备密码认证 - 幽灵设备(App/PC 客户端)性能充足,使用 JWT 认证可以复用现有登录接口,无需预共享密钥 - 动态码绑定:新设备首次上线时,通过 HTTP 接口完成与用户账号的绑定,绑定成功后获取认证信息

1. 普通设备上线认证(设备密码)

┌─────────┐                              ┌─────────┐
│  设备   │                              │  服务器  │
└────┬────┘                              └────┬────┘
     │                                        │
     │  Heartbeat (Type=2)                    │
     │  Username: "user1"                     │
     │  DevicePassword: "Abc12345"            │
     │  CallSign: "" (空)                     │
     │───────────────────────────────────────>│
     │                                        │
     │                                        │ 1. 验证 Username + DevicePassword
     │                                        │ 2. 查询用户信息获取 CallSign
     │                                        │ 3. 检查是否被封禁
     │                                        │
     │  Heartbeat Response                    │
     │  CallSign: "BG1AAA" (服务器填充)        │
     │<───────────────────────────────────────│
     │                                        │
     │  设备缓存 CallSign 用于后续通信          │
     │                                        │

2. 幽灵设备认证(JWT Token)

幽灵设备(App/PC 客户端)使用 JWT Token 进行认证,详见 JWT 认证包格式

3. 新设备动态码绑定流程

新设备首次上线时,通过动态码绑定流程完成与用户账号的关联。此流程适用于没有密码输入功能的设备(如无键盘/触摸屏的嵌入式硬件),用户通过 Web 端输入设备显示的动态码完成绑定。

绑定流程时序图

┌─────────┐                              ┌─────────┐                              ┌─────────┐
│  新设备  │                              │  服务器  │                              │ Web用户 │
└────┬────┘                              └────┬────┘                              └────┬────┘
     │                                        │                                        │
     │  1. POST /api/device/pre-check         │                                        │
     │     { mac, username, device_password } │                                        │
     │───────────────────────────────────────>│                                        │
     │                                        │                                        │
     │     { status: "need_bind" }            │                                        │
     │<───────────────────────────────────────│                                        │
     │                                        │                                        │
     │  2. POST /api/device/request-code      │                                        │
     │     { mac }                            │                                        │
     │───────────────────────────────────────>│                                        │
     │                                        │                                        │
     │     { dynamic_code: "123456",          │                                        │
     │       expires_in: 60 }                 │                                        │
     │<───────────────────────────────────────│                                        │
     │                                        │                                        │
     │  [设备显示动态码: 123456]               │                                        │
     │                                        │                                        │
     │                                        │  3. POST /api/device/bind              │
     │                                        │     { dynamic_code: "123456" }         │
     │                                        │<───────────────────────────────────────│
     │                                        │                                        │
     │                                        │  { device_mac, call_sign }             │
     │                                        │───────────────────────────────────────>│
     │                                        │                                        │
     │                                        │  4. POST /api/device/submit-config     │
     │                                        │     { device_mac, ssid }               │
     │                                        │<───────────────────────────────────────│
     │                                        │                                        │
     │                                        │  { message, udp_auth_info, dmr_id }    │
     │                                        │───────────────────────────────────────>│
     │                                        │                                        │
     │  5. POST /api/device/confirm-bind      │                                        │
     │     { mac }                            │                                        │
     │───────────────────────────────────────>│                                        │
     │                                        │                                        │
     │     { status: "ready",                 │                                        │
     │       username, device_password,       │                                        │
     │       ssid, dmr_id }                   │                                        │
     │<───────────────────────────────────────│                                        │
     │                                        │                                        │
     │  [设备保存配置,准备 UDP 认证]          │                                        │
     │                                        │                                        │

接口说明

接口 方法 认证 说明
/api/device/pre-check POST 设备上电后检查存储的账号密码是否有效
/api/device/request-code POST 请求生成 6 位动态码
/api/device/bind POST JWT 用户通过动态码绑定设备
/api/device/submit-config POST JWT 用户提交设备配置(SSID)
/api/device/confirm-bind POST 设备轮询获取绑定状态和配置

详细步骤

步骤 1:预检查 (pre-check)

设备上电后,使用本地存储的账号密码(如果有)进行预检查: - 验证成功:返回 status: "authenticated",设备可直接进入 UDP 认证流程 - 验证失败/无存储:返回 status: "need_bind",设备进入动态码绑定流程

步骤 2:请求动态码 (request-code)

设备请求生成动态码: - 服务器生成 6 位数字动态码 - 动态码有效期 60 秒 - 设备在屏幕/LED 上显示动态码

步骤 3:用户绑定 (bind)

用户在 Web 端输入设备显示的动态码: - 验证动态码有效性和设备状态 - 将设备 MAC 与用户账号关联 - 返回设备 MAC 和用户呼号

步骤 4:提交配置 (submit-config)

用户为设备配置 SSID: - SSID 范围:1-99 或 106-235(普通设备可用范围) - 服务器将用户名、设备密码、SSID、DMRID 写入设备配置

步骤 5:确认绑定 (confirm-bind)

设备轮询此接口获取配置: - 状态 waiting:等待用户完成绑定 - 状态 ready:绑定完成,返回认证信息

安全机制

  • 动态码单次使用:绑定成功后动态码立即失效
  • 有效期限制:动态码 60 秒过期,绑定状态 10 分钟过期
  • 限速保护:各接口均有独立的限速策略
  • 账号状态校验:用户必须已审核通过才能绑定设备

4. 认证失败处理

  • 连续认证失败 3 次:封禁 10 秒
  • 再次失败:封禁 30 秒
  • 继续失败:封禁 60 秒、300 秒(阶梯递增)

封禁 Key:IP + Username


语音转发流程

┌─────────┐      ┌─────────┐      ┌─────────┐
│ 设备 A  │      │  服务器  │      │ 设备 B  │
│user1    │      │         │      │user2    │
│BG1AAA   │      │         │      │BG2BBB   │
└────┬────┘      └────┬────┘      └────┬────┘
     │                │                │
     │ Opus16K        │                │
     │ Username:user1 │                │
     │ CallSign:BG1AAA│                │
     │───────────────>│                │
     │                │                │
     │                │ 转发(保留头部)│
     │                │───────────────>│
     │                │                │
     │                │          B 显示:
     │                │          "BG1AAA 正在发言"
     │                │          或 "user1 (BG1AAA)"
     │                │                │

字符编码规则

字段 编码 字符集 说明
Username UTF-8 字母、数字、下划线 用户名
DevicePassword ASCII 大小写字母、数字 设备准入密码,6-10 位
CallSign ASCII 大写字母、数字 业余电台呼号
TextMessage UTF-8 任意 Unicode 文本消息

安全限制

UDP 服务端实施以下安全策略,超出限制的数据包将被静默丢弃:

限制项 阈值 说明
最大包体 800 字节 含 90 字节头部,防止异常大包攻击
包速率 150 包/秒 按 IP+Port 维度限速,兼顾 FRP 隧道共享 IP 场景

注意:正常语音通信(合并帧 ~8.3 包/秒,单帧 ~16.7 包/秒)远低于限速阈值,不会受到影响。 150 包/秒的阈值设计考虑了: - FRP/NAT 场景下多设备共享同一出口 IP - 客户端网络抖动后的"追赶发送"突发场景 - 60-180ms 大包架构下丢包的严重影响


版本历史

版本 日期 说明
v1.0 2026-03 初始版本,替代 NRL2 协议
v1.1 2026-03 移除 G.711 编解码支持,统一使用 Opus 16K 格式
v1.2 2026-03 简化协议头,移除 Status 和 SeqNum 字段,头部从 93 字节简化为 90 字节
v1.3 2026-03 优化弱网性能:Opus 帧时长从 20ms 改为 60ms,WebSocket 客户端采用 2 帧合并发送(120ms 间隔)
v1.4 2026-03 新增 JWT 认证方式(Type=1),支持幽灵设备(App/PC 客户端)接入;新增设备型号 104 (macOS)、107 (ESP32);定义 SSID 分配规则
v1.5 2026-03 性能优化:PTT 会话阈值从 200ms 提升到 600ms(适配 60-180ms 大包架构);限速器从 25 PPS 放宽到 150 PPS;放弃批量发送缓冲,改用直接发送;补全 WS→UDP Ghost 路由;修复时长统计
v1.6 2026-03 新增 Config 包协议 (Type=3),支持 UDP 普通设备配置同步(TLV 格式),包含查询、下发、上报、时间同步四种操作
v1.7 2026-03 新增动态码绑定流程,支持无输入能力的普通设备通过 Web 端完成账号绑定