145 lines
4.5 KiB
C
145 lines
4.5 KiB
C
|
|
/**
|
|||
|
|
* @file protocol.h
|
|||
|
|
* @brief SkyLink 多平台实时视频传输 - 公共协议定义
|
|||
|
|
*
|
|||
|
|
* 这个文件定义了机载端(ROS 2)、PC 端(Qt)和移动端(Android)之间的通信协议。
|
|||
|
|
* 必须保持三端的字节对齐和字段顺序一致。
|
|||
|
|
*
|
|||
|
|
* @author SkyLink Team
|
|||
|
|
* @date 2026-01-19
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#pragma once
|
|||
|
|
|
|||
|
|
#include <cstdint>
|
|||
|
|
#include <cstring>
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 强制1字节对齐,避免编译器补齐导致的大小差异
|
|||
|
|
*/
|
|||
|
|
#pragma pack(push, 1)
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @struct PacketHeader
|
|||
|
|
* @brief UDP 数据包头部结构
|
|||
|
|
*
|
|||
|
|
* 总大小: 50 字节
|
|||
|
|
* - 报头识别: 2 字节 (Magic)
|
|||
|
|
* - 帧标识: 4 字节 (FrameID)
|
|||
|
|
* - 分片信息: 4 字节 (TotalChunks + ChunkIndex)
|
|||
|
|
* - 数据长度: 2 字节 (DataLen)
|
|||
|
|
* - 时间戳: 8 字节 (Timestamp)
|
|||
|
|
* - GPS 位置: 24 字节 (Lat, Lon, Alt)
|
|||
|
|
* - 预留: 6 字节 (Reserve 用于将来扩展)
|
|||
|
|
*/
|
|||
|
|
struct PacketHeader {
|
|||
|
|
// ============ 包头识别 ============
|
|||
|
|
uint16_t magic; ///< 魔数 0xAA55 用于识别合法数据包
|
|||
|
|
|
|||
|
|
// ============ 帧标识 ============
|
|||
|
|
uint32_t frame_id; ///< 唯一的帧编号,从 0 开始递增,用于帧同步
|
|||
|
|
|
|||
|
|
// ============ 分片信息 ============
|
|||
|
|
uint16_t total_chunks; ///< 该帧总共被分成多少个 UDP 包
|
|||
|
|
uint16_t chunk_index; ///< 当前数据包是第几个分片 (0-based)
|
|||
|
|
|
|||
|
|
// ============ 载荷信息 ============
|
|||
|
|
uint16_t data_len; ///< 当前包的有效载荷长度(单位:字节)
|
|||
|
|
|
|||
|
|
// ============ 时间戳 ============
|
|||
|
|
double timestamp; ///< GPS 时间戳 (秒数,Unix 时间 或 ROS 时间)
|
|||
|
|
|
|||
|
|
// ============ GPS 位置数据 ============
|
|||
|
|
double lat; ///< 纬度 (WGS84 坐标系)
|
|||
|
|
double lon; ///< 经度 (WGS84 坐标系)
|
|||
|
|
double alt; ///< 海拔高度 (米)
|
|||
|
|
|
|||
|
|
// ============ 校验与预留 ============
|
|||
|
|
uint16_t crc16; ///< CRC16 校验码 (可选,当前未使用)
|
|||
|
|
uint32_t reserve; ///< 保留字段,用于将来的扩展
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取包头大小(字节数)
|
|||
|
|
* @return 包头固定大小:50 字节
|
|||
|
|
*/
|
|||
|
|
static constexpr uint16_t size() {
|
|||
|
|
return sizeof(PacketHeader);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 恢复默认的对齐方式
|
|||
|
|
*/
|
|||
|
|
#pragma pack(pop)
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// 协议常量定义
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
/** @brief 数据包魔数,用于识别合法的 SkyLink 数据包 */
|
|||
|
|
constexpr uint16_t SKYLINK_MAGIC = 0xAA55;
|
|||
|
|
|
|||
|
|
/** @brief 最大的单个 UDP 包大小 (包含报头) - MTU 1500 字节 */
|
|||
|
|
constexpr uint16_t MAX_UDP_PACKET_SIZE = 1472; // 1500 - 20 (IP) - 8 (UDP)
|
|||
|
|
|
|||
|
|
/** @brief 最大的载荷大小 */
|
|||
|
|
constexpr uint16_t MAX_PAYLOAD_SIZE = MAX_UDP_PACKET_SIZE - PacketHeader::size();
|
|||
|
|
|
|||
|
|
/** @brief UDP 接收缓冲区大小 */
|
|||
|
|
constexpr uint32_t RX_BUFFER_SIZE = 65536;
|
|||
|
|
|
|||
|
|
/** @brief 最大支持的分片数 */
|
|||
|
|
constexpr uint16_t MAX_CHUNKS = 1000;
|
|||
|
|
|
|||
|
|
/** @brief 帧超时时间(毫秒),超过此时间的分片将被丢弃 */
|
|||
|
|
constexpr uint32_t FRAME_TIMEOUT_MS = 5000;
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// 图像编码相关常量
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
/** @brief JPEG 图像格式 ID */
|
|||
|
|
constexpr uint8_t IMAGE_FORMAT_JPEG = 0x01;
|
|||
|
|
|
|||
|
|
/** @brief JPEG 最大压缩质量 */
|
|||
|
|
constexpr uint8_t JPEG_MAX_QUALITY = 100;
|
|||
|
|
|
|||
|
|
/** @brief JPEG 最小压缩质量 */
|
|||
|
|
constexpr uint8_t JPEG_MIN_QUALITY = 10;
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// 辅助函数
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 验证包头魔数是否合法
|
|||
|
|
* @param header 待验证的包头结构
|
|||
|
|
* @return 如果魔数为 0xAA55 返回 true,否则返回 false
|
|||
|
|
*/
|
|||
|
|
inline bool isValidPacket(const PacketHeader& header) {
|
|||
|
|
return header.magic == SKYLINK_MAGIC;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 计算 CRC16 校验码 (可选)
|
|||
|
|
* @param data 数据指针
|
|||
|
|
* @param len 数据长度
|
|||
|
|
* @return 16 位 CRC 校验结果
|
|||
|
|
*/
|
|||
|
|
inline uint16_t crc16_ccitt(const uint8_t* data, uint16_t len) {
|
|||
|
|
uint16_t crc = 0xFFFF;
|
|||
|
|
for (uint16_t i = 0; i < len; i++) {
|
|||
|
|
crc ^= (uint16_t)data[i] << 8;
|
|||
|
|
for (int j = 0; j < 8; j++) {
|
|||
|
|
if (crc & 0x8000) {
|
|||
|
|
crc = (crc << 1) ^ 0x1021;
|
|||
|
|
} else {
|
|||
|
|
crc = crc << 1;
|
|||
|
|
}
|
|||
|
|
crc &= 0xFFFF;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return crc;
|
|||
|
|
}
|
|||
|
|
|