车载定位终端为了节省传输带宽、保障数据安全,通常会将定位、状态等信息加密后封装成二进制格式上报,用Python解析这类数据需要先明确终端的通信协议,按步骤完成解密、解析、转换操作。

一、前置准备:明确协议与获取密钥
解析前需要先拿到两个核心信息:一是终端厂商提供的通信协议文档,里面会说明二进制数据的整体结构、每个字段的长度和含义;二是数据加密使用的密钥和算法,常见的加密算法有AES、DES,部分终端会用自定义的异或加密。
假设我们拿到的协议规定数据整体结构如下:
>
| 字段名 | 长度(字节) | 说明 |
|---|---|---|
| 起始符 | 2 | 固定为0x7E 0x7E |
| 数据长度 | 2 | 后续加密数据的总长度 |
| 加密数据 | 可变 | 经过AES加密的定位相关二进制数据 |
| 校验码 | 1 | CRC8校验值 |
二、数据解密处理
首先需要从接收到的完整二进制数据中提取出加密部分,再用对应算法和密钥解密。以下是AES解密的示例代码,假设加密模式为CBC,填充方式为PKCS7:
from Crypto.Cipher import AES
import binascii
def aes_decrypt(encrypted_data, key, iv):
# 初始化AES解密器,CBC模式
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
# 执行解密
decrypted_data = cipher.decrypt(encrypted_data)
# 去除PKCS7填充,填充值为填充的字节数
padding_len = decrypted_data[-1]
return decrypted_data[:-padding_len]
# 示例:假设加密数据、密钥、偏移量如下
encrypted_hex = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" # 实际为终端上报的加密数据十六进制字符串
key = "1234567890abcdef" # 终端对应的加密密钥
iv = "abcdef1234567890" # 加密偏移量
encrypted_bytes = binascii.unhexlify(encrypted_hex)
decrypted_data = aes_decrypt(encrypted_bytes, key, iv)
print("解密后的二进制数据:", decrypted_data)三、解析二进制字段提取原始值
解密后得到的是明文的二进制数据,接下来需要用Python的struct模块按照协议定义的字段顺序和格式拆分数据。假设协议规定解密后的数据结构为:
- 经度:4字节,有符号整数,单位为1/1000000度
- 纬度:4字节,有符号整数,单位为1/1000000度
- 速度:2字节,无符号整数,单位为0.1km/h
- 时间戳:4字节,无符号整数,单位为秒,从2020-01-01 00:00:00开始计算
解析代码如下:
import struct
import datetime
def parse_location_data(decrypted_data):
# 按照协议格式解析,>表示大端字节序,i为有符号4字节,I为无符号4字节,H为无符号2字节
# 假设前4字节经度,接下来4字节纬度,接下来2字节速度,最后4字节时间戳
lon_raw, lat_raw, speed_raw, timestamp_raw = struct.unpack('>iiHI', decrypted_data[:14])
return {
"lon_raw": lon_raw,
"lat_raw": lat_raw,
"speed_raw": speed_raw,
"timestamp_raw": timestamp_raw
}
# 解析上一步得到的解密数据
parsed = parse_location_data(decrypted_data)
print("解析出的原始字段值:", parsed)四、原始值转换为可用定位信息
拆分得到的原始值是协议定义的缩放数值,需要按照规则转换为实际的定位信息:
def convert_to_real_info(parsed_data):
# 经度转换:原始值除以1000000得到度数
lon = parsed_data["lon_raw"] / 1000000
# 纬度转换:原始值除以1000000得到度数
lat = parsed_data["lat_raw"] / 1000000
# 速度转换:原始值乘以0.1得到km/h
speed = parsed_data["speed_raw"] * 0.1
# 时间戳转换:从2020-01-01 00:00:00开始加上对应秒数
base_time = datetime.datetime(2020, 1, 1, 0, 0, 0)
real_time = base_time + datetime.timedelta(seconds=parsed_data["timestamp_raw"])
return {
"经度": round(lon, 6),
"纬度": round(lat, 6),
"速度(km/h)": round(speed, 1),
"定位时间": real_time.strftime("%Y-%m-%d %H:%M:%S")
}
# 转换为可用信息
real_info = convert_to_real_info(parsed)
print("最终定位信息:", real_info)注意事项
实际开发中需要注意几个问题:一是不同厂商的终端协议差异很大,一定要以对应终端的官方协议文档为准;二是二进制数据的字节序(大端/小端)要和协议一致,否则解析结果会完全错误;三是如果数据有校验机制,解析前先校验数据完整性,避免处理错误数据。
如果遇到自定义加密算法,需要先通过抓包、逆向等方式明确加密逻辑,再对应实现解密方法,整体解析流程和上述步骤一致。