空域数据管理
功能说明
开启空域数据实时上报后,可从您的服务提供商处获取他们提供的Webhook地址以及加密Token,输入并保存后即可通过司空API实时推送飞行器的动态数据至第三方,以下是三方服务商的接入指南。
接入指南
数据上报协议
1. 请求报文头 (Request Header)
| 字段名 | 字段代码 | 数据类型 | 说明 | 必填 |
|---|---|---|---|---|
| 数据源 | source | Integer | 1: 无人机直报2: 制造商系统代报3: 加装模块代报4: 采集设备代报 | 是 |
| 报送平台 | platform | String | 平台名称(如 DJI FLY) | 是 |
| 程序版本 | programVersion | String | 版本号(如 version1.0) | 是 |
| 签名 | signature | String | 生成的请求签名 | 是 |
| 请求时间戳 | timestamp | String | 请求unix时间戳(秒级) | 是 |
| 随机字符串 | nonce | String | 随机生成的字符串 | 是 |
签名生成流程
输入参数
token: 应用密钥(由对接的服务商提供,用户配置到司空侧),长度要求32-64 字符,由英文字母、数字和特殊符号组成,且必须包含三种以上类型的组合。timestamp: 请求时间戳(从请求头timestamp获取)nonce: 随机字符串(从请求头nonce获取)body: 请求体原始字节数据(如 JSON 序列化后的RequestBody)
签名规则
- 将
timestamp、nonce和body按顺序拼接成字符串(无分隔符) - 使用 HMAC-SHA256 算法,以
token为密钥,对拼接后的字符串加密 - 将加密结果转换为 十六进制小写字符串
Go 代码实现
import (
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
)
func GenerateHMACSignature(appKey, timestamp, nonce string, body []byte) string {
mac := hmac.New(sha256.New, []byte(appKey))
mac.Write([]byte(timestamp))
mac.Write([]byte(nonce))
mac.Write(body)
return hex.EncodeToString(mac.Sum(nil))
}
签名验证流程
验证逻辑
- 服务端从请求头获取:
signature: 客户端生成的签名timestamp: 请求时间戳nonce: 随机字符串
- 服务端使用相同参数和密钥
appKey重新生成签名 - 通过 常量时间比较 验证客户端签名与服务端生成签名是否一致
Go 代码实现
func VerifyHMACSignature(appKey, timestamp, nonce string, body []byte, signature string) bool {
expected := GenerateHMACSignature(appKey, timestamp, nonce, body)
return subtle.ConstantTimeCompare([]byte(expected), []byte(signature)) == 1
}
2. 请求体 (Request Body)
JSON 格式
{
"app_id":"default",
"flight_hub_organization_id":"xxxx",
"paths": [
{ /* FlightPath 对象 */ },
{ /* FlightPath 对象 */ }
]
}
字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_id | string | 是 | 应用唯一标识符,当前默认为 default |
flight_hub_organization_id | string | 是 | 用户的司空组织ID |
paths | FlightPath[] | 是 | 飞行路径数据数组(对象结构见下方) |
FlightPathRecord 结构体说明
该结构体用于描述飞行路径点数据,以下是字段详细说明:
| 序号 | 字段名称 | 字段代码 | 数据类型 | 说明 | 是否必填 |
|---|---|---|---|---|---|
| 1 | 飞行记录编号 | order_id | String | 格式:厂商序列号(sn)-8位起飞日期(YYYYMMDD)-8位随机码示例: 1581F5FHD25G100C1SDN-20240320-owvGyLqe整个飞行过程使用同一个ID | 是 |
| 2 | 无人机序列号 | sn | String | 厂商的无人机生产序列号 | 是 |
| 3 | 飞行状态 | flight_status | String | TakeOff:起飞点Inflight:飞行中Land:降落点 | 是 |
| 4 | 统一社会信用代码 | manufacturer_id | String | 生产厂商的统一社会信用代码 | 是 |
| 5 | 无人驾驶航空器实名登记号 | uas_id | String | 若无登记号,使用默认值 UAS-DEFAULT | 是 |
| 6 | 当前时间 | time_stamp | String | UTC+8 时间,格式 yyyyMMddHHmmss示例: 20240124095001 | 是 |
| 7 | 产品型号 | uas_model | String | 字母/数字/符号组合 | 是 |
| 8 | 坐标系类型 | coordinate | Integer | 1:WGS-842:CGCS20003:GLONASS-PZ90 | 是 |
| 9 | 经度 | longitude | Long | 单位:度(°),实际值 = 字段值 ÷ 10⁷ | 是 |
| 10 | 纬度 | latitude | Long | 单位:度(°),实际值 = 字段值 ÷ 10⁷ | 是 |
| 11 | 真高类型 | height_type | Integer | 1:相对起飞点高度(ALT)2:相对当前位置高(AGL | 是 |
| 12 | 真高 | height | Integer | 单位:米(m),实际值 = 字段值 ÷ 10 | 是 |
| 13 | 海拔高度 | altitude | Integer | 单位:米(m),实际值 = 字段值 ÷ 10 | 是 |
| 14 | 垂直飞行速度值 | vs | Integer | 单位:m/s,实际值 = 字段值 ÷ 10 | 是 |
| 15 | 水平飞行速度值 | gs | Integer | 单位:m/s,实际值 = 字段值 ÷ 10 | 是 |
| 16 | 航迹角 | course | Integer | 真北顺时针角度(0°-360°),实际值 = 字段值 ÷ 10 无数据时填 -999 | 是 |
| 17 | RID广播状态 | remote_id_status | Integer | 0 关闭 1 开启 | 是 |
Body 示例 :
{
"app_id": "default",
"flight_hub_organization_id": "105ddf05-54ce-4626-a872-cb32b8c98864",
"paths": [{
"order_id": "1529F6QAD23CB00A42DP-20251028-89W2aK2W",
"sn": "1529F6QAD23CB00A42DP",
"flight_status": "Land",
"manufacturer_id": "925413007954257495",
"uas_id": "UAS-DEFAULT",
"time_stamp": "20251028115000",
"uas_model": "M3D",
"coordinate": 1,
"longitude": 1243579943,
"latitude": 217931488,
"height_type": 1,
"height": 0,
"altitude": 187,
"vs": 0,
"gs": 0,
"course": 1229,
"remote_id_status": 1
}]
}
3. 响应报文体 (Response Body)
| 字段名 | 字段代码 | 数据类型 | 说明 |
|---|---|---|---|
| 返回状态 | code | String | 0=成功400=失败 |
| 结果描述 | result | String | 详细描述(如 success) |
响应示例:
{
"code": "0",
"result": "success"
}