first commit
This commit is contained in:
0
code/vdes_utils/__init__.py
Normal file
0
code/vdes_utils/__init__.py
Normal file
73
code/vdes_utils/local_inspection_monitor.py
Normal file
73
code/vdes_utils/local_inspection_monitor.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import struct
|
||||
from datetime import datetime
|
||||
|
||||
class LocalInspectionMonitor:
|
||||
def __init__(self):
|
||||
self.hello = "地检状态监控报文解析工具类"
|
||||
|
||||
|
||||
def get_local_inspection_status_data(self, demod_data):
|
||||
"""解析地检状态监控数据"""
|
||||
# 格式字符串:>表示大端序,B表示Uint8,H表示Uint16,I表示Uint32
|
||||
format_str = '>B B I I B B B I I I B I I B B B B B B B B I B B I B H B B B I B B B B B I B B B I B B B I B B B I B I B I' # 总共82字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:102])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['cpu0_usage'] = fields[0] # CPU0占用率
|
||||
res['cpu1_usage'] = fields[1] # CPU1占用率
|
||||
res['program_version'] = fields[2] # 地检/终端程序版本号
|
||||
res['uptime'] = fields[3] # 开机后运行时间
|
||||
res['gps_status'] = fields[4] # GPS状态
|
||||
res['pps_lock_status'] = fields[5] # 内部PPS锁定状态
|
||||
res['ad9361_status'] = fields[6] # AD9361状态
|
||||
res['ad9361_calibration_factor'] = fields[7] # AD9361频率校准因子
|
||||
res['ad9361_tx_lo_freq'] = fields[8] # AD9361发射本振频率
|
||||
res['ad9361_tx_sample_rate'] = fields[9] # AD9361发射采样率
|
||||
res['ad9361_tx_attenuation'] = fields[10] # AD9361发射衰减
|
||||
res['ad9361_rx_lo_freq'] = fields[11] # AD9361接收本振频率
|
||||
res['ad9361_rx_sample_rate'] = fields[12] # AD9361接收采样率
|
||||
res['ad9361_rx_gain'] = fields[13] # AD9361接收增益
|
||||
res['vdes_protocol_enable'] = fields[14] # VDES协议工作使能
|
||||
res['vdes_filter_enable'] = fields[15] # VDES报文过滤使能
|
||||
res['rac_limit_enable'] = fields[16] # 随机信道消息接入限制使能
|
||||
res['vdes_agc_enable'] = fields[17] # VDES接收AGC使能
|
||||
res['delay_compensation_enable'] = fields[18] # 星地延时补偿使能
|
||||
res['upper_satellite_id'] = fields[19] # 上半频段卫星ID
|
||||
res['upper_satellite_cqi'] = fields[20] # 上半频段卫星CQI
|
||||
res['upper_satellite_amplitude'] = fields[21] # 上半频段卫星信号平均幅度
|
||||
res['lower_satellite_id'] = fields[22] # 下半频段卫星ID
|
||||
res['lower_satellite_cqi'] = fields[23] # 下半频段卫星CQI
|
||||
res['lower_satellite_amplitude'] = fields[24] # 下半频段卫星信号平均幅度
|
||||
res['current_satellite_id'] = fields[25] # 当前选用的卫星ID
|
||||
res['current_vdes_slot'] = fields[26] # 当前VDES时隙号
|
||||
res['current_vdes_subframe'] = fields[27] # 当前VDES子帧编号
|
||||
res['vdes_tx_linkid'] = fields[28] # VDES发射LinKID
|
||||
res['vdes_tx_channel'] = fields[29] # VDES发射通道
|
||||
res['vdes_tx_frequency'] = fields[30] # VDES发射频点
|
||||
res['vdes_buffer1_status'] = fields[31] # VDES上行数据缓存区1状态
|
||||
res['vdes_buffer2_status'] = fields[32] # VDES上行数据缓存区2状态
|
||||
res['demod1_enable'] = fields[33] # VDES解调器1使能
|
||||
res['demod1_linkid'] = fields[34] # VDES解调器1接收LINKID
|
||||
res['demod1_channel'] = fields[35] # VDES解调器1接收信道
|
||||
res['demod1_frequency'] = fields[36] # VDES解调器1接收频点
|
||||
res['demod2_enable'] = fields[37] # VDES解调器2使能
|
||||
res['demod2_linkid'] = fields[38] # VDES解调器2接收LINKID
|
||||
res['demod2_channel'] = fields[39] # VDES解调器2接收信道
|
||||
res['demod2_frequency'] = fields[40] # VDES解调器2接收频点
|
||||
res['demod3_enable'] = fields[41] # VDES解调器3使能
|
||||
res['demod3_linkid'] = fields[42] # VDES解调器3接收LINKID
|
||||
res['demod3_channel'] = fields[43] # VDES解调器3接收信道
|
||||
res['demod3_frequency'] = fields[44] # VDES解调器3接收频点
|
||||
res['demod4_enable'] = fields[45] # VDES解调器4使能
|
||||
res['demod4_linkid'] = fields[46] # VDES解调器4接收LINKID
|
||||
res['demod4_channel'] = fields[47] # VDES解调器4接收信道
|
||||
res['demod4_frequency'] = fields[48] # VDES解调器4接收频点
|
||||
res['ais_demod1_enable'] = fields[49] # AIS解调器1使能
|
||||
res['ais_demod1_frequency'] = fields[50] # AIS解调器1频点
|
||||
res['ais_demod2_enable'] = fields[51] # AIS解调器2使能
|
||||
res['ais_demod2_frequency'] = fields[52] # AIS解调器2频点
|
||||
|
||||
return res
|
||||
368
code/vdes_utils/vdes_data_parsing.py
Normal file
368
code/vdes_utils/vdes_data_parsing.py
Normal file
@@ -0,0 +1,368 @@
|
||||
import struct
|
||||
import logging
|
||||
from django.db import transaction
|
||||
from vdes_utils.vdes_demod_data import VdesDemodData
|
||||
from vdes_utils.local_inspection_monitor import LocalInspectionMonitor
|
||||
from django.core.cache import cache
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class VdesDataParsing:
|
||||
def __init__(self):
|
||||
self.hello = "VDES数据解析工具类"
|
||||
self.vdes_demod_data_parsing = VdesDemodData() #VDES解调数据解析类
|
||||
self.local_inspection_monitor = LocalInspectionMonitor() #地检状态监控数据解析类
|
||||
self.payload_fragments = [] # 用于存储长消息分片数据的数组
|
||||
|
||||
def parse_vdes_data(self, packet_data):
|
||||
from system.models import GpsStatus, HistorySession, Contacts, Content, MessageFlag
|
||||
from datetime import datetime
|
||||
import time
|
||||
#判断帧头是否符合琢研
|
||||
# print(packet_data.hex())
|
||||
if packet_data[:4].decode('ascii') == '####':
|
||||
# print("开始解析报文帧")
|
||||
packet_type = struct.unpack('>I', packet_data[4:8])[0] #获取报文类型 7为gps, 12为ais
|
||||
packet_lenth = struct.unpack('>I', packet_data[8:12])[0]
|
||||
packet_content = packet_data[12:]
|
||||
print('报文类型:', packet_type)
|
||||
if packet_type == 7:
|
||||
# print(f"GPS内容长度: {packet_lenth} 字节")
|
||||
data = self.get_gps_data(packet_content)
|
||||
utc_time = data.get('utc_time', 0)
|
||||
EPOCH_DIFF = 1577836800 # 2020-01-01 00:00:00 到 1970-01-01 00:00:00 的秒数差
|
||||
try:
|
||||
if utc_time:
|
||||
# utc_time 是从 1980-01-06 开始的秒数
|
||||
unix_timestamp = utc_time + EPOCH_DIFF
|
||||
utc_time_dt = datetime.fromtimestamp(unix_timestamp)
|
||||
else:
|
||||
utc_time_dt = datetime.now()
|
||||
except (ValueError, TypeError, OSError):
|
||||
utc_time_dt = datetime.now()
|
||||
# print(data)
|
||||
# print(utc_time_dt)
|
||||
#下面代码还应该根据正负判断前端应该显示南北纬东西经
|
||||
GpsStatus.objects.create(
|
||||
utc_time=utc_time_dt,
|
||||
longitude=data.get('longitude', 0),
|
||||
latitude=data.get('latitude', 0),
|
||||
altitude=data.get('altitude', 0),
|
||||
used_satellites=data.get('used_sats', 10),
|
||||
gps_satellites=data.get('gps_sats', 6),
|
||||
beidou_satellites=data.get('beidou_sats', 4),
|
||||
gps_status=data.get('gps_status', 1),
|
||||
fix_mode=data.get('position_mode', 3),
|
||||
hdop=data.get('hdop', 1.2),
|
||||
speed=data.get('speed', 0),
|
||||
direction=data.get('direction', 0)
|
||||
)
|
||||
elif packet_type == 8:
|
||||
print(f"地检内容长度: {packet_lenth} 字节")
|
||||
data = self.local_inspection_monitor.get_local_inspection_status_data(packet_content)
|
||||
cache.set("local_inspection_data", data, timeout=None) #存入redis缓存中,让另一个进程能获取
|
||||
|
||||
elif packet_type == 12:
|
||||
# print(packet_content)
|
||||
data = self.get_ais_vdes_data(packet_content)
|
||||
if data['message_type'] != 12:
|
||||
return
|
||||
# print(data)
|
||||
if data['demodulator_id'] <= 5: #0-5为VDES解调数据
|
||||
# 检查demod_data的前四个字节是否为'#$%','#$%'为本系统判断数据(文本图片视频)类型的标识
|
||||
if len(data['demod_data']) >= 4 and data['demod_data'][:4] == b'#$%':
|
||||
print("检测到特殊标识符'#$%',打印数据:", data['demod_data'])
|
||||
else:
|
||||
self.vdes_demod_data_parsing.main(data['demod_data'])
|
||||
# print(f"VDES内容长度: {packet_lenth} 字节")
|
||||
|
||||
# print(f"VDES内容长度: {packet_lenth} 字节")
|
||||
# demod_data = self.get_vdes_demod_data(data['demod_data'])
|
||||
# print('VDES解调数据:', demod_data)
|
||||
# print("----------------------------")
|
||||
# time.sleep(0.1)
|
||||
# # 此处需要判断消息帧类型,卫星公告牌之类的----------------------------------------
|
||||
# combined_data = self.vdes_data_processing(demod_data)
|
||||
|
||||
|
||||
# # 此处需要判断接收的消息数据格式----------------------------------------
|
||||
# if combined_data is not None:
|
||||
# # print("完整消息:", combined_data)
|
||||
# # 保存为图片文件
|
||||
# import os
|
||||
# from datetime import datetime
|
||||
|
||||
# # 创建保存目录(如果不存在)
|
||||
# save_dir = 'E:\projects\wxwx\projects_job\VDES_Projects\报文模拟解析\地检设备模拟\\tcp_data'
|
||||
# os.makedirs(save_dir, exist_ok=True)
|
||||
|
||||
# # 生成文件名(带时间戳)
|
||||
# timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
# filename = os.path.join(save_dir, f'received_image_{timestamp}.jpg') # 假设是JPEG格式
|
||||
|
||||
# # 写入文件
|
||||
# with open(filename, 'wb') as f:
|
||||
# f.write(combined_data)
|
||||
# print(f"图片已保存到: {filename}")
|
||||
# return
|
||||
|
||||
|
||||
|
||||
# if combined_data is not None:
|
||||
# MMSI = demod_data['Dest_id']
|
||||
# print(f"发送者MMSI: {MMSI}")
|
||||
# print(f"消息: {combined_data}")
|
||||
# # 查询或创建通讯录记录
|
||||
# contact, created = Contacts.objects.get_or_create(MMSI=MMSI)
|
||||
# Id = contact.Id
|
||||
# contact, created = HistorySession.objects.get_or_create(FormId=Id, MMSI=MMSI)
|
||||
# content = Content.objects.create(
|
||||
# SendId=Id,
|
||||
# ReciverId=1,
|
||||
# Content="<p>"+combined_data+"</p>",
|
||||
# Type=0, # 0=html文本
|
||||
# State=1, # 已发送
|
||||
# NoCode=str(int(time.time())) , # 你可以放时间戳
|
||||
# CreateDateUtc=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
# ReadFlag=0,
|
||||
# SoundStatus=0
|
||||
# )
|
||||
# flag = MessageFlag.objects.first()
|
||||
# if flag:
|
||||
# flag.has_new_message = True
|
||||
# flag.save()
|
||||
|
||||
|
||||
else: #6/7为AIS解调数据
|
||||
try:
|
||||
demod_data = self.get_ais_demod_data(data['demod_data'])
|
||||
# print(demod_data)
|
||||
# 处理并存储AIS解调数据
|
||||
self.ais_data_processing(demod_data)
|
||||
except ValueError as e:
|
||||
print(f"跳过不支持的AIS消息: {e}")
|
||||
|
||||
def get_gps_data(self, packet_content):
|
||||
# 解析前40字节的GPS信息
|
||||
fields = struct.unpack('>QiiiHHHBBIII', packet_content[:40])
|
||||
# 解析卫星状态数据
|
||||
satellite_data = []
|
||||
remaining_data = packet_content[40:]
|
||||
|
||||
# 每6字节为一组卫星数据
|
||||
for i in range(0, len(remaining_data), 6):
|
||||
satellite_bytes = remaining_data[i:i+6]
|
||||
if len(satellite_bytes) < 6:
|
||||
break # 不足6字节的数据丢弃
|
||||
|
||||
# 解包卫星数据
|
||||
sat_type, prn, snr, elevation, azimuth = struct.unpack('>BBBBH', satellite_bytes)
|
||||
|
||||
satellite_data.append({
|
||||
'satellite_type': sat_type, # 卫星类型
|
||||
'prn': prn, # 卫星PRN码
|
||||
'snr': snr, # 卫星信噪比(dBHz)
|
||||
'elevation': elevation, # 卫星仰角(0-90度)
|
||||
'azimuth': azimuth # 卫星方位角(0-359度)
|
||||
})
|
||||
res = {}
|
||||
# 将度分格式转换为十进制度格式
|
||||
dm_to_decimal = lambda raw: (1 if raw >= 0 else -1) * (
|
||||
int(abs(raw) // 10000000) + (abs(raw) % 10000000) / 10000000 / 60 * 100
|
||||
)
|
||||
res['utc_time'] = fields[0] # UTC时间 (8字节)
|
||||
res['longitude'] = round(dm_to_decimal(fields[1]), 7)
|
||||
res['latitude'] = round(dm_to_decimal(fields[2]), 7)
|
||||
res['altitude'] = fields[3]/10 # 海拔高度 (4字节,50米)
|
||||
res['used_sats'] = fields[4] # 定位使用的卫星数 (2字节)
|
||||
res['gps_sats'] = fields[5] # GPS卫星数 (2字节)
|
||||
res['beidou_sats'] = fields[6] # 北斗卫星数 (2字节)
|
||||
res['gps_status'] = fields[7] # GPS状态 (1字节,单点定位)
|
||||
res['position_mode'] = fields[8] # 定位模式 (1字节,3D定位)
|
||||
res['hdop'] = fields[9]/10 # 水平精度因子 (4字节,25.5米)
|
||||
res['speed'] = fields[10]/100 # 运动速度 (4字节,10.24节)
|
||||
res['direction'] = fields[11]/100 # 运动方向 (4字节,359度)
|
||||
res['satellite_data'] = satellite_data # 卫星状态数据列表
|
||||
return res
|
||||
|
||||
def get_ais_vdes_data(self, packet_content):
|
||||
res ={}
|
||||
demod_packet = packet_content[:11]
|
||||
demod_data = packet_content[11:]
|
||||
fields = struct.unpack('>BBBHHHH', demod_packet)
|
||||
res['message_type'] = fields[0]
|
||||
res['satellite_id'] = fields[1]
|
||||
res['demodulator_id'] = fields[2]
|
||||
res['demod_slot'] = fields[3]
|
||||
res['receive_delay'] = fields[4]
|
||||
snr_packed = fields[5]
|
||||
res['frequency_bias'] = fields[6]
|
||||
# 解析SNR
|
||||
snr_int = (snr_packed >> 8) & 0xFF
|
||||
snr_frac = snr_packed & 0xFF
|
||||
res['snr'] = snr_int + snr_frac / 100.0
|
||||
res['demod_data'] = demod_data
|
||||
# bit_string = "".join(f"{byte:08b}" for byte in demod_data)
|
||||
# print(bit_string)
|
||||
# print(demod_data.hex())
|
||||
|
||||
return res
|
||||
|
||||
def get_ais_demod_data(self, bit_string):
|
||||
# 如果输入是字节类型,先转换为二进制字符串
|
||||
if isinstance(bit_string, bytes):
|
||||
bit_string = ''.join(format(byte, '08b') for byte in bit_string)
|
||||
pos = 0
|
||||
decoded = {}
|
||||
|
||||
decoded['message_id'] = int(bit_string[pos:pos+6], 2) # 消息ID (6 bits)
|
||||
# 检查message_id是否为1、2或3,如果不是则抛出异常 (当前程序只解析了位置报告,还不支持其他ais报文)
|
||||
if decoded['message_id'] not in [1, 2, 3]:
|
||||
raise ValueError(f"Unsupported message_id: {decoded['message_id']}")
|
||||
pos += 6
|
||||
decoded['repeat_indicator'] = int(bit_string[pos:pos+2], 2) # 转发指示符 (2 bits)
|
||||
pos += 2
|
||||
decoded['user_id'] = int(bit_string[pos:pos+30], 2) # 用户ID/MMSI (30 bits)
|
||||
pos += 30
|
||||
decoded['navigation_status'] = int(bit_string[pos:pos+4], 2) # 导航状态 (4 bits)
|
||||
pos += 4
|
||||
|
||||
# 旋转速率 (8位有符号整数)
|
||||
rot_value = int(bit_string[pos:pos+8], 2)
|
||||
if rot_value > 127:
|
||||
rot_value -= 256
|
||||
decoded['rot'] = rot_value
|
||||
pos += 8
|
||||
|
||||
decoded['sog'] = int(bit_string[pos:pos+10], 2) # 地面航速 (10 bits)
|
||||
pos += 10
|
||||
decoded['position_accuracy'] = int(bit_string[pos], 2) # 位置准确度 (1 bit)
|
||||
pos += 1
|
||||
|
||||
# 经度 (28位)
|
||||
longitude_value = int(bit_string[pos:pos+28], 2)
|
||||
decoded['longitude'] = longitude_value / (60 * 10000) # 转换为度
|
||||
pos += 28
|
||||
|
||||
# 纬度 (27位)
|
||||
latitude_value = int(bit_string[pos:pos+27], 2)
|
||||
decoded['latitude'] = latitude_value / (60 * 10000) # 转换为度
|
||||
pos += 27
|
||||
|
||||
decoded['cog'] = int(bit_string[pos:pos+12], 2) # 地面航线 (12 bits)
|
||||
pos += 12
|
||||
decoded['true_heading'] = int(bit_string[pos:pos+9], 2) # 实际航向 (9 bits)
|
||||
pos += 9
|
||||
decoded['timestamp'] = int(bit_string[pos:pos+6], 2) # 时戳 (6 bits)
|
||||
pos += 6
|
||||
decoded['special_maneuver'] = int(bit_string[pos:pos+2], 2) # 特定操纵指示符 (2 bits)
|
||||
pos += 2
|
||||
decoded['spare'] = int(bit_string[pos:pos+3], 2) # 备用 (3 bits)
|
||||
pos += 3
|
||||
decoded['raim_flag'] = int(bit_string[pos], 2) # RAIM标志 (1 bit)
|
||||
pos += 1
|
||||
decoded['comm_state'] = int(bit_string[pos:pos+19], 2) # 通信状态 (19 bits)
|
||||
|
||||
return decoded
|
||||
|
||||
def get_vdes_demod_data(self, packed_data):
|
||||
"""解码VDES解调信息字段"""
|
||||
header_size = struct.calcsize('>B H I B B I H')
|
||||
header = packed_data[:header_size]
|
||||
Type, payload_size, source_id, satellite_id, session_id, dest_id, fragment_num = \
|
||||
struct.unpack('>B H I B B I H', header)
|
||||
# 计算负载字节长度进去截取,去除帧尾
|
||||
payload = packed_data[header_size:header_size+payload_size - 12]
|
||||
# 将UTF-8编码的二进制payload解码为字符串
|
||||
try:
|
||||
decoded_payload = payload.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
decoded_payload = payload # 如果解码失败,保留原始二进制数据
|
||||
|
||||
return {
|
||||
'Type': Type,
|
||||
'Payload_size': payload_size,
|
||||
'Source_id': source_id,
|
||||
'Satellite_id': satellite_id,
|
||||
'Session_id': session_id,
|
||||
'Dest_id': dest_id,
|
||||
'Fragment_num': fragment_num,
|
||||
'Payload': decoded_payload # 返回解码后的字符串
|
||||
}
|
||||
|
||||
def ais_data_processing(self, demod_data):
|
||||
from system.models import ShipLatestStatus
|
||||
from datetime import datetime
|
||||
# 转换timestamp为datetime对象
|
||||
timestamp = demod_data.get('timestamp', 0)
|
||||
try:
|
||||
timestamp_dt = datetime.fromtimestamp(timestamp) if timestamp else datetime.now()
|
||||
except (ValueError, TypeError):
|
||||
timestamp_dt = datetime.now()
|
||||
|
||||
ship_data = {
|
||||
'mmsi': str(demod_data.get('user_id', '000000000')),
|
||||
'latitude': demod_data.get('latitude', 0),
|
||||
'longitude': demod_data.get('longitude', 0),
|
||||
'speed': demod_data.get('sog', 0),
|
||||
'course': demod_data.get('cog', 0),
|
||||
'heading': demod_data.get('true_heading', 0),
|
||||
'nav_status': demod_data.get('navigation_status', 0),
|
||||
'timestamp': timestamp_dt, # 使用转换后的datetime对象
|
||||
'ship_name': '未知',
|
||||
'ship_type': 0,
|
||||
'draught': 0,
|
||||
'destination': '未知'
|
||||
}
|
||||
with transaction.atomic():
|
||||
# 尝试获取已有记录
|
||||
ship, created = ShipLatestStatus.objects.get_or_create(
|
||||
mmsi=ship_data['mmsi'],
|
||||
defaults=ship_data
|
||||
)
|
||||
|
||||
if not created:
|
||||
# 更新已有记录
|
||||
for field, value in ship_data.items():
|
||||
setattr(ship, field, value)
|
||||
ship.save()
|
||||
|
||||
|
||||
# def vdes_data_processing(self, demod_data):
|
||||
# """处理VDES分片数据"""
|
||||
# fragment_num = demod_data['Fragment_num']
|
||||
# print(self.payload_fragments)
|
||||
# if demod_data['Type'] == 30: # 开始分片
|
||||
# self.payload_fragments = [] # 清空数组
|
||||
|
||||
# elif demod_data['Type'] == 31: # 数据分片
|
||||
# # 确保数组足够大以容纳当前分片
|
||||
# while len(self.payload_fragments) <= fragment_num:
|
||||
# self.payload_fragments.append(None)
|
||||
# self.payload_fragments[fragment_num] = demod_data['Payload']
|
||||
|
||||
# elif demod_data['Type'] == 32: # 结束分片
|
||||
# # 拼接所有有效分片
|
||||
# combined_payload = ''.join([p for p in self.payload_fragments if p is not None])
|
||||
# return combined_payload
|
||||
# return None # 如果不是结束分片,返回None
|
||||
def vdes_data_processing(self, demod_data):
|
||||
"""处理VDES分片数据"""
|
||||
fragment_num = demod_data['Fragment_num']
|
||||
# print(self.payload_fragments)
|
||||
if demod_data['Type'] == 30: # 开始分片
|
||||
print("------------------开始——————————————————————————————————————————————")
|
||||
self.payload_fragments = [] # 清空数组
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
|
||||
elif demod_data['Type'] == 31: # 数据分片
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
|
||||
elif demod_data['Type'] == 32: # 结束分片
|
||||
# 拼接所有有效分片
|
||||
print("------------------结束——————————————————————————————————————————————")
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
combined_payload = b''.join([p for p in self.payload_fragments if p is not None])
|
||||
print(len(self.payload_fragments))
|
||||
return combined_payload
|
||||
return None # 如果不是结束分片,返回None
|
||||
727
code/vdes_utils/vdes_demod_data.py
Normal file
727
code/vdes_utils/vdes_demod_data.py
Normal file
@@ -0,0 +1,727 @@
|
||||
import struct
|
||||
import time
|
||||
import os
|
||||
from datetime import datetime
|
||||
from django.core.cache import cache
|
||||
|
||||
class VdesDemodData:
|
||||
def __init__(self):
|
||||
self.hello = "VDES/AIS解调数据解析工具类"
|
||||
# 存储会话数据,key是 (source_id, satellite_id, session_id, dest_id)
|
||||
# value = {"fragments": [...], "total": N}
|
||||
self.sessions = {}
|
||||
self.payload_fragments = [] # 用于存储长消息分片数据的数组
|
||||
|
||||
|
||||
def main(self, demod_data, file_name=None):
|
||||
self.file_name = file_name
|
||||
demod_data_type = self.get_data_type(demod_data)
|
||||
print('demod_data_type:', demod_data_type)
|
||||
# 判断解调数据是什么类型,类型有卫星公告牌、消息片段等
|
||||
if demod_data_type == 30 or demod_data_type == 31 or demod_data_type == 32:
|
||||
# 解析出消息片段中的字段
|
||||
message_fragment = self.get_message_demod_data(demod_data)
|
||||
print(message_fragment)
|
||||
# 处理消息片段
|
||||
# self.message_fragment_processing(message_fragment)
|
||||
elif demod_data_type == 1:
|
||||
sbb1 = self.get_sbb_fragment_data_1(demod_data)
|
||||
print(sbb1)
|
||||
cache.set("sbb1", sbb1, timeout=None) #存入redis缓存中,让另一个进程能获取
|
||||
elif demod_data_type == 2:
|
||||
sbb2 = self.get_sbb_fragment_data_2(demod_data)
|
||||
print(sbb2)
|
||||
cache.set("sbb2", sbb2, timeout=None)
|
||||
elif demod_data_type == 3:
|
||||
sbb3 = self.get_sbb_fragment_data_3(demod_data)
|
||||
print(sbb3)
|
||||
cache.set("sbb3", sbb3, timeout=None)
|
||||
elif demod_data_type == 4:
|
||||
sbb4 = self.get_sbb_fragment_data_4(demod_data)
|
||||
print(sbb4)
|
||||
cache.set("sbb4", sbb4, timeout=None)
|
||||
elif demod_data_type == 5:
|
||||
sbb5 = self.get_sbb_fragment_data_5(demod_data)
|
||||
print(sbb5)
|
||||
cache.set("sbb5", sbb5, timeout=None)
|
||||
elif demod_data_type == 6:
|
||||
sbb6 = self.get_sbb_fragment_data_6(demod_data)
|
||||
print(sbb6)
|
||||
cache.set("sbb6", sbb6, timeout=None)
|
||||
elif demod_data_type == 10:
|
||||
sbb10 = self.get_sbb_fragment_data_10(demod_data)
|
||||
print(sbb10)
|
||||
elif demod_data_type == 11:
|
||||
sbb11 = self.get_sbb_fragment_data_11(demod_data)
|
||||
print(sbb11)
|
||||
elif demod_data_type == 12:
|
||||
sbb12 = self.get_sbb_fragment_data_12(demod_data)
|
||||
print(sbb12)
|
||||
elif demod_data_type == 18:
|
||||
sbb18 = self.get_sbb_fragment_data_18(demod_data)
|
||||
print(sbb18)
|
||||
elif demod_data_type == 20:
|
||||
sbb20 = self.get_sbb_fragment_data_20(demod_data)
|
||||
print(sbb20)
|
||||
|
||||
|
||||
|
||||
def get_data_type(self, demod_data):
|
||||
"""从二进制数据中提取第一个字节作为类型字段"""
|
||||
if demod_data:
|
||||
data_type = struct.unpack('>B', demod_data[:1])[0]
|
||||
return data_type
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def get_message_demod_data(self, packed_data):
|
||||
"""解码VDES解调信息字段并分类存储"""
|
||||
header_size = struct.calcsize('>B H I B B I H')
|
||||
header = packed_data[:header_size]
|
||||
Type, payload_size, source_id, satellite_id, session_id, dest_id, remainder_fragment_num = \
|
||||
struct.unpack('>B H I B B I H', header)
|
||||
|
||||
# 提取payload(减去12字节帧尾)
|
||||
payload = packed_data[header_size:header_size + payload_size - 12]
|
||||
|
||||
# 尝试解码为字符串,否则保留二进制
|
||||
# try:
|
||||
# decoded_payload = payload.decode('utf-8')
|
||||
# except UnicodeDecodeError:
|
||||
# decoded_payload = payload
|
||||
|
||||
session_key = (source_id, satellite_id, session_id, dest_id)
|
||||
|
||||
# ---------- Type = 30 会话开始 ----------
|
||||
if Type == 30:
|
||||
total_fragments = remainder_fragment_num + 1
|
||||
self.sessions[session_key] = {
|
||||
"fragments": [None] * total_fragments,
|
||||
"total": total_fragments,
|
||||
"end_flag": False,
|
||||
"end_time": None
|
||||
}
|
||||
# 起始片段必然是索引 0
|
||||
self.sessions[session_key]["fragments"][0] = payload
|
||||
|
||||
# 如果 remainder_fragment_num=0,说明只有这一个片段
|
||||
if remainder_fragment_num == 0:
|
||||
return self.finalize_session(session_key)
|
||||
|
||||
# ---------- Type = 31 会话延续 ----------
|
||||
elif Type == 31:
|
||||
if session_key not in self.sessions:
|
||||
raise ValueError(f"收到延续片段但会话未初始化: {session_key}")
|
||||
session = self.sessions[session_key]
|
||||
index = remainder_fragment_num # 延续片段索引 = remainder_fragment_num
|
||||
session["fragments"][index] = payload
|
||||
|
||||
# ---------- Type = 32 会话结束 ----------
|
||||
elif Type == 32:
|
||||
if session_key not in self.sessions:
|
||||
raise ValueError(f"收到结束片段但会话未初始化: {session_key}")
|
||||
session = self.sessions[session_key]
|
||||
index = remainder_fragment_num # 结束片段索引 = remainder_fragment_num
|
||||
session["fragments"][index] = payload
|
||||
session["end_flag"] = True
|
||||
session["end_time"] = time.time()
|
||||
|
||||
# 如果所有片段收齐,立刻完成
|
||||
if all(frag is not None for frag in session["fragments"]):
|
||||
return self.finalize_session(session_key, force=True)
|
||||
# 检查超时的会话
|
||||
self.check_timeouts()
|
||||
return {
|
||||
'Type': Type,
|
||||
'Payload_size': payload_size,
|
||||
'Source_id': source_id,
|
||||
'Satellite_id': satellite_id,
|
||||
'Session_id': session_id,
|
||||
'Dest_id': dest_id,
|
||||
'Remainder_Fragment_num': remainder_fragment_num,
|
||||
'Payload': payload
|
||||
}
|
||||
|
||||
def check_timeouts(self):
|
||||
"""检查会话是否超时 40 秒"""
|
||||
now = time.time()
|
||||
to_release = []
|
||||
for key, session in self.sessions.items():
|
||||
if session["end_flag"] and session["end_time"]:
|
||||
if now - session["end_time"] > 40:
|
||||
to_release.append(key)
|
||||
|
||||
for key in to_release:
|
||||
print(f"会话 {key} 超时未收齐,强制释放")
|
||||
self.finalize_session(key, force=True)
|
||||
|
||||
def finalize_session(self, session_key, force=False):
|
||||
"""拼接并释放一个会话"""
|
||||
session = self.sessions[session_key]
|
||||
full_data = b""
|
||||
mmsi = session_key[0] #source_id为MMSI号
|
||||
for frag in session["fragments"]:
|
||||
if frag is None and not force:
|
||||
# 不完整且不强制 → 暂不释放
|
||||
return None
|
||||
if frag:
|
||||
full_data += frag
|
||||
# if isinstance(frag, str):
|
||||
# full_data += frag.encode("utf-8")
|
||||
# elif isinstance(frag, bytes):
|
||||
# full_data += frag
|
||||
if full_data is not None:
|
||||
# 保存为图片文件
|
||||
self.message_fragment_processing(full_data, mmsi)
|
||||
del self.sessions[session_key]
|
||||
|
||||
print(f"✅ 会话 {session_key} 完成, 数据长度={len(full_data)}")
|
||||
return {"Session_key": session_key, "Full_data": full_data}
|
||||
|
||||
def message_fragment_processing(self, message, mmsi):
|
||||
from system.models import HistorySession, Contacts, Content, MessageFlag
|
||||
# 此处需要判断接收的消息数据格式----------------------------------------
|
||||
if message is not None:
|
||||
print('-------------------------------------------')
|
||||
# print("完整消息:", combined_data)
|
||||
# 创建保存目录(如果不存在) - 使用Django静态资源路径
|
||||
save_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'static', 'vdes_data')
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
|
||||
# 生成文件名(带时间戳) - 使用self.file_name作为后缀名
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
file_extension = getattr(self, 'file_name', 'jpg') # 默认使用txt作为后缀名
|
||||
if file_extension is None:
|
||||
file_extension = 'jpg'
|
||||
print(f"文件后缀名: {file_extension}")
|
||||
# 根据文件后缀判断文件类型
|
||||
file_type = self.get_file_type(file_extension)
|
||||
if file_type == 0:
|
||||
try:
|
||||
# 直接解码消息内容
|
||||
if isinstance(message, bytes):
|
||||
content_text = message.decode('utf-8')
|
||||
else:
|
||||
content_text = str(message)
|
||||
except Exception as e:
|
||||
print(f"保存数据库记录时出错: {e}")
|
||||
content_text = '无法解码的文本内容'
|
||||
content = "<p>"+content_text+"</p>"
|
||||
else:
|
||||
filename = os.path.join(save_dir, f'received_file_{timestamp}.{file_extension}')
|
||||
|
||||
# 写入文件
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(message)
|
||||
print(f"文件已保存到: {filename}")
|
||||
print(f"发送者MMSI: {mmsi}")
|
||||
# 构建文件的相对URL路径(相对于Django服务器)
|
||||
content = f"/static/vdes_data/received_file_{timestamp}.{file_extension}"
|
||||
|
||||
|
||||
# 将文件路径存入数据库------------------------------------------------------
|
||||
try:
|
||||
# 查询或创建通讯录记录
|
||||
contact, created = Contacts.objects.get_or_create(MMSI=mmsi)
|
||||
Id = contact.Id
|
||||
contact, created = HistorySession.objects.get_or_create(FormId=Id, MMSI=mmsi)
|
||||
|
||||
# 将文件路径保存到Content表中
|
||||
content = Content.objects.create(
|
||||
SendId=Id,
|
||||
ReciverId=1,
|
||||
Content=content, # 保存文件路径或文本内容
|
||||
Type=file_type, # 2=文件类型
|
||||
State=1, # 已发送
|
||||
NoCode=str(int(time.time())), # 时间戳
|
||||
CreateDateUtc=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
ReadFlag=0,
|
||||
SoundStatus=0
|
||||
)
|
||||
# 设置新消息标志,用于前端右上角新消息提示
|
||||
flag = MessageFlag.objects.first()
|
||||
if flag:
|
||||
flag.has_new_message = True
|
||||
flag.save()
|
||||
|
||||
print(f"文件路径已保存到数据库: {filename}")
|
||||
except Exception as e:
|
||||
print(f"保存数据库记录时出错: {e}")
|
||||
return
|
||||
if combined_data is not None:
|
||||
MMSI = demod_data['Dest_id']
|
||||
print(f"发送者MMSI: {MMSI}")
|
||||
print(f"消息: {combined_data}")
|
||||
# 查询或创建通讯录记录
|
||||
contact, created = Contacts.objects.get_or_create(MMSI=MMSI)
|
||||
Id = contact.Id
|
||||
contact, created = HistorySession.objects.get_or_create(FormId=Id, MMSI=MMSI)
|
||||
content = Content.objects.create(
|
||||
SendId=Id,
|
||||
ReciverId=1,
|
||||
Content="<p>"+combined_data+"</p>",
|
||||
Type=0, # 0=html文本
|
||||
State=1, # 已发送
|
||||
NoCode=str(int(time.time())) , # 你可以放时间戳
|
||||
CreateDateUtc=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
ReadFlag=0,
|
||||
SoundStatus=0
|
||||
)
|
||||
flag = MessageFlag.objects.first()
|
||||
if flag:
|
||||
flag.has_new_message = True
|
||||
flag.save()
|
||||
def get_file_type(self, file_extension):
|
||||
"""
|
||||
根据文件后缀判断文件类型
|
||||
:param file_extension: 文件后缀
|
||||
:return: 文件类型 (0: txt, 1: 图片, 2: 视频)
|
||||
"""
|
||||
# 文本文件
|
||||
if file_extension.lower() in ['txt']:
|
||||
return 0
|
||||
|
||||
# 图片文件
|
||||
if file_extension.lower() in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']:
|
||||
return 1
|
||||
|
||||
# 视频文件
|
||||
if file_extension.lower() in ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv']:
|
||||
return 2
|
||||
|
||||
# 默认返回二进制文件类型
|
||||
return 0
|
||||
def message_fragment_splicing(self, demod_data):
|
||||
"""拼接消息分片数据"""
|
||||
fragment_num = demod_data['Fragment_num']
|
||||
# print(self.payload_fragments)
|
||||
if demod_data['Type'] == 30: # 开始分片
|
||||
print("------------------开始——————————————————————————————————————————————")
|
||||
self.payload_fragments = [] # 清空数组
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
|
||||
elif demod_data['Type'] == 31: # 数据分片
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
|
||||
elif demod_data['Type'] == 32: # 结束分片
|
||||
# 拼接所有有效分片
|
||||
print("------------------结束——————————————————————————————————————————————")
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
combined_payload = b''.join([p for p in self.payload_fragments if p is not None])
|
||||
print(len(self.payload_fragments))
|
||||
return combined_payload
|
||||
return None # 如果不是结束分片,返回None
|
||||
|
||||
|
||||
def get_sbb_fragment_data_1(self, demod_data):
|
||||
"""解析类型为1的公告牌起始片段数据"""
|
||||
format_str = '>B B B B H I H B H H B H' # 总共20字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:20])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['satellite_id'] = fields[1] # 卫星ID
|
||||
res['primary_network_id'] = fields[2] # 主网络ID
|
||||
res['roaming_network_id'] = fields[3] # 漫游网络ID
|
||||
res['sbb_version'] = fields[4] # SBB版本
|
||||
res['start_time'] = fields[5] # 开始时间
|
||||
res['validity_period'] = fields[6] # 可用期限
|
||||
|
||||
# 解析服务能力字段
|
||||
capability_field = fields[7] #服务能力
|
||||
res['itu_compatibility'] = (capability_field >> 4) & 0x0F # ITU-R M.2092建议书版兼容
|
||||
res['reserved_bits'] = capability_field & 0x0F # 4个LSB保留位
|
||||
|
||||
res['sbb_spare_frequency'] = fields[8] # SBB备用频率
|
||||
res['uplink_max_length'] = fields[9] # 上行链路消息最大长度
|
||||
res['reserved'] = fields[10] # 保留字段
|
||||
res['total_message_length'] = fields[11] # 消息总长度
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_2(self, demod_data):
|
||||
"""解析类型为2的公告牌片段数据"""
|
||||
format_str = '>B H H B 6B 6B H H B 6B 6B' # 总共35字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['downlink_frequency_a'] = fields[1] # 下行链路中心频率A
|
||||
res['uplink_frequency_a'] = fields[2] # 上行链路中心频率A
|
||||
|
||||
# 解析带宽A字段
|
||||
bandwidth_a = fields[3]
|
||||
res['downlink_bandwidth_a'] = (bandwidth_a >> 4) & 0x0F # 下行链路带宽A
|
||||
res['uplink_bandwidth_a'] = bandwidth_a & 0x0F # 上行链路带宽A
|
||||
|
||||
channel_slot_lengths_a = []
|
||||
for i in range(4, 10): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_a.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_a'] = channel_slot_lengths_a
|
||||
|
||||
# 解析逻辑信道功能A (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_a = []
|
||||
for i in range(10, 16): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_a.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_a'] = channel_functions_a
|
||||
|
||||
res['downlink_frequency_b'] = fields[16] # 下行链路中心频率B
|
||||
res['uplink_frequency_b'] = fields[17] # 上行链路中心频率B
|
||||
|
||||
# 解析带宽B字段
|
||||
bandwidth_b = fields[18]
|
||||
res['downlink_bandwidth_b'] = (bandwidth_b >> 4) & 0x0F # 下行链路带宽B
|
||||
res['uplink_bandwidth_b'] = bandwidth_b & 0x0F # 上行链路带宽B
|
||||
|
||||
# 解析逻辑信道时隙长度B (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_b = []
|
||||
for i in range(19, 25): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_b.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_b'] = channel_slot_lengths_b
|
||||
|
||||
# 解析逻辑信道功能B (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_b = []
|
||||
for i in range(25, 31): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_b.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_b'] = channel_functions_b
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_3(self, demod_data):
|
||||
"""解析类型为3的公告牌片段数据(逻辑信道24-47定义,频率对C和D)"""
|
||||
format_str = '>B H H B 6B 6B H H B 6B 6B' # 总共35字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['downlink_frequency_c'] = fields[1] # 下行链路中心频率C
|
||||
res['uplink_frequency_c'] = fields[2] # 上行链路中心频率C
|
||||
|
||||
# 解析带宽C字段
|
||||
bandwidth_c = fields[3]
|
||||
res['downlink_bandwidth_c'] = (bandwidth_c >> 4) & 0x0F # 下行链路带宽C
|
||||
res['uplink_bandwidth_c'] = bandwidth_c & 0x0F # 上行链路带宽C
|
||||
|
||||
# 解析逻辑信道时隙长度C (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_c = []
|
||||
for i in range(4, 10): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_c.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_c'] = channel_slot_lengths_c
|
||||
|
||||
# 解析逻辑信道功能C (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_c = []
|
||||
for i in range(10, 16): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_c.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_c'] = channel_functions_c
|
||||
|
||||
res['downlink_frequency_d'] = fields[16] # 下行链路中心频率D
|
||||
res['uplink_frequency_d'] = fields[17] # 上行链路中心频率D
|
||||
|
||||
# 解析带宽D字段
|
||||
bandwidth_d = fields[18]
|
||||
res['downlink_bandwidth_d'] = (bandwidth_d >> 4) & 0x0F # 下行链路带宽D
|
||||
res['uplink_bandwidth_d'] = bandwidth_d & 0x0F # 上行链路带宽D
|
||||
|
||||
# 解析逻辑信道时隙长度D (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_d = []
|
||||
for i in range(19, 25): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_d.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_d'] = channel_slot_lengths_d
|
||||
|
||||
# 解析逻辑信道功能D (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_d = []
|
||||
for i in range(25, 31): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_d.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_d'] = channel_functions_d
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_4(self, demod_data):
|
||||
"""解析类型为4的公告牌片段数据(逻辑信道48-71定义,频率对E和F)"""
|
||||
format_str = '>B H H B 6B 6B H H B 6B 6B' # 总共35字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['downlink_frequency_e'] = fields[1] # 下行链路中心频率E
|
||||
res['uplink_frequency_e'] = fields[2] # 上行链路中心频率E
|
||||
|
||||
# 解析带宽E字段
|
||||
bandwidth_e = fields[3]
|
||||
res['downlink_bandwidth_e'] = (bandwidth_e >> 4) & 0x0F # 下行链路带宽E
|
||||
res['uplink_bandwidth_e'] = bandwidth_e & 0x0F # 上行链路带宽E
|
||||
|
||||
# 解析逻辑信道时隙长度E (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_e = []
|
||||
for i in range(4, 10): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_e.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_e'] = channel_slot_lengths_e
|
||||
|
||||
# 解析逻辑信道功能E (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_e = []
|
||||
for i in range(10, 16): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_e.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_e'] = channel_functions_e
|
||||
|
||||
res['downlink_frequency_f'] = fields[16] # 下行链路中心频率F
|
||||
res['uplink_frequency_f'] = fields[17] # 上行链路中心频率F
|
||||
|
||||
# 解析带宽F字段
|
||||
bandwidth_f = fields[18]
|
||||
res['downlink_bandwidth_f'] = (bandwidth_f >> 4) & 0x0F # 下行链路带宽F
|
||||
res['uplink_bandwidth_f'] = bandwidth_f & 0x0F # 上行链路带宽F
|
||||
|
||||
# 解析逻辑信道时隙长度F (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_f = []
|
||||
for i in range(19, 25): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_f.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_f'] = channel_slot_lengths_f
|
||||
|
||||
# 解析逻辑信道功能F (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_f = []
|
||||
for i in range(25, 31): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_f.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_f'] = channel_functions_f
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_5(self, demod_data):
|
||||
"""解析类型为5的公告牌片段数据(SBB数字签名第1部分)"""
|
||||
format_str = '>B 32s' # 总共33字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:33])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['digital_signature_part1'] = fields[1].hex() # 数字签名第1部分(16进制字符串格式)
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_6(self, demod_data):
|
||||
"""解析类型为6的公告牌片段数据(SBB数字签名第2部分)"""
|
||||
format_str = '>B 32s' # 总共33字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:33])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['digital_signature_part1'] = fields[1].hex() # 数字签名第2部分(16进制字符串格式)
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_10(self, demod_data):
|
||||
"""解析类型为10的媒体访问控制数据"""
|
||||
format_str = '>B H B B B B B B B B H' # 总共13字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:13])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['satellite_id'] = fields[2] # 卫星ID
|
||||
res['primary_network_id'] = fields[3] # 主网络ID
|
||||
res['roaming_network_id'] = fields[4] # 漫游网络ID
|
||||
res['media_access_priority'] = fields[5] # 媒体接入优先级
|
||||
res['random_selection_interval'] = fields[6] # 随机选择间隔
|
||||
res['rac_message_access_limit'] = fields[7] # RAC消息接入限制
|
||||
res['network_status'] = fields[8] # 网络状态
|
||||
|
||||
# 解析自动重复请求/超时限制字段
|
||||
arq_timeout_field = fields[9]
|
||||
res['fragment_retry_count'] = (arq_timeout_field >> 4) & 0x0F # 4个MSB:片段重试次数
|
||||
res['timeout_timer_setting'] = arq_timeout_field & 0x0F # 4个LSB:超时定时器设置
|
||||
|
||||
res['announcement_version_number'] = fields[10] # 公告版本号
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def get_sbb_fragment_data_11(self, demod_data):
|
||||
"""解析寻呼数据(类型11)"""
|
||||
# 总共35字节 (1+2+4+4+4+4+4+4+4+4)
|
||||
format_str = '>B H I I I I I I I I'
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['vessel_station_id_1'] = fields[2] # 1号船舶电台ID
|
||||
res['vessel_station_id_2'] = fields[3] # 2号船舶电台ID
|
||||
res['vessel_station_id_3'] = fields[4] # 3号船舶电台ID
|
||||
res['vessel_station_id_4'] = fields[5] # 4号船舶电台ID
|
||||
res['vessel_station_id_5'] = fields[6] # 5号船舶电台ID
|
||||
res['vessel_station_id_6'] = fields[7] # 6号船舶电台ID
|
||||
res['vessel_station_id_7'] = fields[8] # 7号船舶电台ID
|
||||
res['vessel_station_id_8'] = fields[9] # 8号船舶电台ID
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_12(self, demod_data):
|
||||
"""解析资源分配数据(类型12)"""
|
||||
# 总共37字节 (1+2+4+1+1+1+1 + 4+1+1+1+1 + 4+1+1+1+1 + 4+1+1+1+1)
|
||||
format_str = '>B H I B B B B I B B B B I B B B B I B B B B'
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['vessel_station_id_1'] = fields[2] # 船舶电台ID 1
|
||||
res['logical_channel_1'] = fields[3] # 逻辑信道1
|
||||
res['link_id_1'] = fields[4] # 链路ID 1
|
||||
res['session_id_1'] = fields[5] # 会话ID 1
|
||||
res['uplink_cqi_1'] = fields[6] # 上行链路链路CQI 1
|
||||
res['vessel_station_id_2'] = fields[7] # 船舶电台ID 2
|
||||
res['logical_channel_2'] = fields[8] # 逻辑信道2
|
||||
res['link_id_2'] = fields[9] # 链路ID 2
|
||||
res['session_id_2'] = fields[10] # 会话ID 2
|
||||
res['uplink_cqi_2'] = fields[11] # 上行链路链路CQI 2
|
||||
res['vessel_station_id_3'] = fields[12] # 船舶电台ID 3
|
||||
res['logical_channel_3'] = fields[13] # 逻辑信道3
|
||||
res['link_id_3'] = fields[14] # 链路ID 3
|
||||
res['session_id_3'] = fields[15] # 会话ID 3
|
||||
res['uplink_cqi_3'] = fields[16] # 上行链路链路CQI 3
|
||||
res['vessel_station_id_4'] = fields[17] # 船舶电台ID 4
|
||||
res['logical_channel_4'] = fields[18] # 逻辑信道4
|
||||
res['link_id_4'] = fields[19] # 链路ID 4
|
||||
res['session_id_4'] = fields[20] # 会话ID 4
|
||||
res['uplink_cqi_4'] = fields[21] # 上行链路链路CQI 4
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_18(self, demod_data):
|
||||
"""解析送达通知数据(类型18)"""
|
||||
# 总共34字节 (1+2+1+4+1+4+1+4+1+4+1+4+1+4+1)
|
||||
format_str = '>B H B I B I B I B I B I B I B'
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:34])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['satellite_id'] = fields[2] # 卫星ID
|
||||
res['vessel_station_id_1'] = fields[3] # 1号船舶电台ID
|
||||
res['session_id_1'] = fields[4] # 1号船舶会话ID
|
||||
res['vessel_station_id_2'] = fields[5] # 2号船舶电台ID
|
||||
res['session_id_2'] = fields[6] # 2号船舶会话ID
|
||||
res['vessel_station_id_3'] = fields[7] # 3号船舶电台ID
|
||||
res['session_id_3'] = fields[8] # 3号船舶会话ID
|
||||
res['vessel_station_id_4'] = fields[9] # 4号船舶电台ID
|
||||
res['session_id_4'] = fields[10] # 4号船舶会话ID
|
||||
res['vessel_station_id_5'] = fields[11] # 5号船舶电台ID
|
||||
res['session_id_5'] = fields[12] # 5号船舶会话ID
|
||||
res['vessel_station_id_6'] = fields[13] # 6号船舶电台ID
|
||||
res['session_id_6'] = fields[14] # 6号船舶会话ID
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_20(self, demod_data):
|
||||
"""解析资源请求数据(类型20)"""
|
||||
format_str = '>B I B B B B B' # 总共10字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:10])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['vessel_station_id'] = fields[1] # 船舶电台ID
|
||||
res['satellite_id'] = fields[2] # 卫星ID
|
||||
|
||||
# 解析优先级和消息长度字段
|
||||
priority_length_field = fields[3]
|
||||
res['priority'] = (priority_length_field >> 4) & 0x0F # 比特7-4,优先级
|
||||
res['message_length'] = priority_length_field & 0x0F # 比特3-0,消息长度
|
||||
|
||||
res['terminal_capability'] = fields[4] # 终端能力
|
||||
res['downlink_asc_cqi'] = fields[5] # 下行链路ASC CQI
|
||||
res['tbd'] = fields[6] # TBD
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
571
code/vdes_utils/vdes_demod_data_bk.py
Normal file
571
code/vdes_utils/vdes_demod_data_bk.py
Normal file
@@ -0,0 +1,571 @@
|
||||
import struct
|
||||
|
||||
class VdesDemodData:
|
||||
def __init__(self):
|
||||
self.hello = "VDES/AIS解调数据解析工具类"
|
||||
|
||||
self.payload_fragments = [] # 用于存储长消息分片数据的数组
|
||||
|
||||
|
||||
def main(self, demod_data):
|
||||
demod_data_type = self.get_data_type(demod_data)
|
||||
# print('demod_data_type:', demod_data_type)
|
||||
# 判断解调数据是什么类型,类型有卫星公告牌、消息片段等
|
||||
if demod_data_type == 30 or demod_data_type == 31 or demod_data_type == 32:
|
||||
# 解析出消息片段中的字段
|
||||
message_fragment = self.get_message_demod_data(demod_data)
|
||||
# 处理消息片段
|
||||
self.message_fragment_processing(message_fragment)
|
||||
# elif demod_data_type == 1:
|
||||
# sbb1 = self.get_sbb_fragment_data_1(demod_data)
|
||||
# print(sbb1)
|
||||
# elif demod_data_type == 2:
|
||||
# sbb2 = self.get_sbb_fragment_data_2(demod_data)
|
||||
# print(sbb2)
|
||||
# elif demod_data_type == 3:
|
||||
# sbb3 = self.get_sbb_fragment_data_3(demod_data)
|
||||
# print(sbb3)
|
||||
# elif demod_data_type == 4:
|
||||
# sbb4 = self.get_sbb_fragment_data_4(demod_data)
|
||||
# print(sbb4)
|
||||
# elif demod_data_type == 5:
|
||||
# sbb5 = self.get_sbb_fragment_data_5(demod_data)
|
||||
# print(sbb5)
|
||||
# elif demod_data_type == 6:
|
||||
# sbb6 = self.get_sbb_fragment_data_6(demod_data)
|
||||
# print(sbb6)
|
||||
# elif demod_data_type == 10:
|
||||
# sbb10 = self.get_sbb_fragment_data_10(demod_data)
|
||||
# print(sbb10)
|
||||
# elif demod_data_type == 11:
|
||||
# sbb11 = self.get_sbb_fragment_data_11(demod_data)
|
||||
# print(sbb11)
|
||||
# elif demod_data_type == 12:
|
||||
# sbb12 = self.get_sbb_fragment_data_12(demod_data)
|
||||
# print(sbb12)
|
||||
# elif demod_data_type == 18:
|
||||
# sbb18 = self.get_sbb_fragment_data_18(demod_data)
|
||||
# print(sbb18)
|
||||
# elif demod_data_type == 20:
|
||||
# sbb20 = self.get_sbb_fragment_data_20(demod_data)
|
||||
# print(sbb20)
|
||||
|
||||
|
||||
|
||||
def get_data_type(self, demod_data):
|
||||
"""从二进制数据中提取第一个字节作为类型字段"""
|
||||
if demod_data:
|
||||
data_type = struct.unpack('>B', demod_data[:1])[0]
|
||||
return data_type
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def get_message_demod_data(self, packed_data):
|
||||
"""解码VDES解调信息字段"""
|
||||
header_size = struct.calcsize('>B H I B B I H')
|
||||
header = packed_data[:header_size]
|
||||
Type, payload_size, source_id, satellite_id, session_id, dest_id, remainder_fragment_num = \
|
||||
struct.unpack('>B H I B B I H', header)
|
||||
# 计算负载字节长度进去截取,去除帧尾
|
||||
print(header_size)
|
||||
print(payload_size)
|
||||
payload = packed_data[header_size:header_size+payload_size-12]
|
||||
print('packed_data:', packed_data.hex())
|
||||
print('payload:', payload.hex())
|
||||
# 将UTF-8编码的二进制payload解码为字符串
|
||||
try:
|
||||
decoded_payload = payload.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
decoded_payload = payload # 如果解码失败,保留原始二进制数据
|
||||
|
||||
return {
|
||||
'Type': Type,
|
||||
'Payload_size': payload_size,
|
||||
'Source_id': source_id,
|
||||
'Satellite_id': satellite_id,
|
||||
'Session_id': session_id,
|
||||
'Dest_id': dest_id,
|
||||
'Remainder_Fragment_num': remainder_fragment_num,
|
||||
'Payload': decoded_payload # 返回解码后的字符串
|
||||
}
|
||||
|
||||
def message_fragment_processing(self, message_fragment):
|
||||
|
||||
combined_data = self.message_fragment_splicing(message_fragment)
|
||||
# 此处需要判断接收的消息数据格式----------------------------------------
|
||||
if combined_data is not None:
|
||||
# print("完整消息:", combined_data)
|
||||
# 保存为图片文件
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# 创建保存目录(如果不存在)
|
||||
save_dir = 'E:\projects\wxwx\projects_job\VDES_Projects\报文模拟解析\地检设备模拟\\tcp_data'
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
|
||||
# 生成文件名(带时间戳)
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
filename = os.path.join(save_dir, f'received_image_{timestamp}.jpg') # 假设是JPEG格式
|
||||
|
||||
# 写入文件
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(combined_data)
|
||||
print(f"图片已保存到: {filename}")
|
||||
return
|
||||
if combined_data is not None:
|
||||
MMSI = demod_data['Dest_id']
|
||||
print(f"发送者MMSI: {MMSI}")
|
||||
print(f"消息: {combined_data}")
|
||||
# 查询或创建通讯录记录
|
||||
contact, created = Contacts.objects.get_or_create(MMSI=MMSI)
|
||||
Id = contact.Id
|
||||
contact, created = HistorySession.objects.get_or_create(FormId=Id, MMSI=MMSI)
|
||||
content = Content.objects.create(
|
||||
SendId=Id,
|
||||
ReciverId=1,
|
||||
Content="<p>"+combined_data+"</p>",
|
||||
Type=0, # 0=html文本
|
||||
State=1, # 已发送
|
||||
NoCode=str(int(time.time())) , # 你可以放时间戳
|
||||
CreateDateUtc=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
ReadFlag=0,
|
||||
SoundStatus=0
|
||||
)
|
||||
flag = MessageFlag.objects.first()
|
||||
if flag:
|
||||
flag.has_new_message = True
|
||||
flag.save()
|
||||
def message_fragment_splicing(self, demod_data):
|
||||
"""拼接消息分片数据"""
|
||||
fragment_num = demod_data['Fragment_num']
|
||||
# print(self.payload_fragments)
|
||||
if demod_data['Type'] == 30: # 开始分片
|
||||
print("------------------开始——————————————————————————————————————————————")
|
||||
self.payload_fragments = [] # 清空数组
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
|
||||
elif demod_data['Type'] == 31: # 数据分片
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
|
||||
elif demod_data['Type'] == 32: # 结束分片
|
||||
# 拼接所有有效分片
|
||||
print("------------------结束——————————————————————————————————————————————")
|
||||
self.payload_fragments.append(demod_data['Payload'])
|
||||
combined_payload = b''.join([p for p in self.payload_fragments if p is not None])
|
||||
print(len(self.payload_fragments))
|
||||
return combined_payload
|
||||
return None # 如果不是结束分片,返回None
|
||||
|
||||
|
||||
def get_sbb_fragment_data_1(self, demod_data):
|
||||
"""解析类型为1的公告牌起始片段数据"""
|
||||
format_str = '>B B B B H I H B H H B H' # 总共20字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:20])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['satellite_id'] = fields[1] # 卫星ID
|
||||
res['primary_network_id'] = fields[2] # 主网络ID
|
||||
res['roaming_network_id'] = fields[3] # 漫游网络ID
|
||||
res['sbb_version'] = fields[4] # SBB版本
|
||||
res['start_time'] = fields[5] # 开始时间
|
||||
res['validity_period'] = fields[6] # 可用期限
|
||||
|
||||
# 解析服务能力字段
|
||||
capability_field = fields[7] #服务能力
|
||||
res['itu_compatibility'] = (capability_field >> 4) & 0x0F # ITU-R M.2092建议书版兼容
|
||||
res['reserved_bits'] = capability_field & 0x0F # 4个LSB保留位
|
||||
|
||||
res['sbb_spare_frequency'] = fields[8] # SBB备用频率
|
||||
res['uplink_max_length'] = fields[9] # 上行链路消息最大长度
|
||||
res['reserved'] = fields[10] # 保留字段
|
||||
res['total_message_length'] = fields[11] # 消息总长度
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_2(self, demod_data):
|
||||
"""解析类型为2的公告牌片段数据"""
|
||||
format_str = '>B H H B 6B 6B H H B 6B 6B' # 总共35字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['downlink_frequency_a'] = fields[1] # 下行链路中心频率A
|
||||
res['uplink_frequency_a'] = fields[2] # 上行链路中心频率A
|
||||
|
||||
# 解析带宽A字段
|
||||
bandwidth_a = fields[3]
|
||||
res['downlink_bandwidth_a'] = (bandwidth_a >> 4) & 0x0F # 下行链路带宽A
|
||||
res['uplink_bandwidth_a'] = bandwidth_a & 0x0F # 上行链路带宽A
|
||||
|
||||
channel_slot_lengths_a = []
|
||||
for i in range(4, 10): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_a.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_a'] = channel_slot_lengths_a
|
||||
|
||||
# 解析逻辑信道功能A (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_a = []
|
||||
for i in range(10, 16): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_a.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_a'] = channel_functions_a
|
||||
|
||||
res['downlink_frequency_b'] = fields[16] # 下行链路中心频率B
|
||||
res['uplink_frequency_b'] = fields[17] # 上行链路中心频率B
|
||||
|
||||
# 解析带宽B字段
|
||||
bandwidth_b = fields[18]
|
||||
res['downlink_bandwidth_b'] = (bandwidth_b >> 4) & 0x0F # 下行链路带宽B
|
||||
res['uplink_bandwidth_b'] = bandwidth_b & 0x0F # 上行链路带宽B
|
||||
|
||||
# 解析逻辑信道时隙长度B (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_b = []
|
||||
for i in range(19, 25): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_b.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_b'] = channel_slot_lengths_b
|
||||
|
||||
# 解析逻辑信道功能B (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_b = []
|
||||
for i in range(25, 31): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_b.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_b'] = channel_functions_b
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_3(self, demod_data):
|
||||
"""解析类型为3的公告牌片段数据(逻辑信道24-47定义,频率对C和D)"""
|
||||
format_str = '>B H H B 6B 6B H H B 6B 6B' # 总共35字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['downlink_frequency_c'] = fields[1] # 下行链路中心频率C
|
||||
res['uplink_frequency_c'] = fields[2] # 上行链路中心频率C
|
||||
|
||||
# 解析带宽C字段
|
||||
bandwidth_c = fields[3]
|
||||
res['downlink_bandwidth_c'] = (bandwidth_c >> 4) & 0x0F # 下行链路带宽C
|
||||
res['uplink_bandwidth_c'] = bandwidth_c & 0x0F # 上行链路带宽C
|
||||
|
||||
# 解析逻辑信道时隙长度C (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_c = []
|
||||
for i in range(4, 10): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_c.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_c'] = channel_slot_lengths_c
|
||||
|
||||
# 解析逻辑信道功能C (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_c = []
|
||||
for i in range(10, 16): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_c.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_c'] = channel_functions_c
|
||||
|
||||
res['downlink_frequency_d'] = fields[16] # 下行链路中心频率D
|
||||
res['uplink_frequency_d'] = fields[17] # 上行链路中心频率D
|
||||
|
||||
# 解析带宽D字段
|
||||
bandwidth_d = fields[18]
|
||||
res['downlink_bandwidth_d'] = (bandwidth_d >> 4) & 0x0F # 下行链路带宽D
|
||||
res['uplink_bandwidth_d'] = bandwidth_d & 0x0F # 上行链路带宽D
|
||||
|
||||
# 解析逻辑信道时隙长度D (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_d = []
|
||||
for i in range(19, 25): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_d.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_d'] = channel_slot_lengths_d
|
||||
|
||||
# 解析逻辑信道功能D (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_d = []
|
||||
for i in range(25, 31): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_d.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_d'] = channel_functions_d
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_4(self, demod_data):
|
||||
"""解析类型为4的公告牌片段数据(逻辑信道48-71定义,频率对E和F)"""
|
||||
format_str = '>B H H B 6B 6B H H B 6B 6B' # 总共35字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['downlink_frequency_e'] = fields[1] # 下行链路中心频率E
|
||||
res['uplink_frequency_e'] = fields[2] # 上行链路中心频率E
|
||||
|
||||
# 解析带宽E字段
|
||||
bandwidth_e = fields[3]
|
||||
res['downlink_bandwidth_e'] = (bandwidth_e >> 4) & 0x0F # 下行链路带宽E
|
||||
res['uplink_bandwidth_e'] = bandwidth_e & 0x0F # 上行链路带宽E
|
||||
|
||||
# 解析逻辑信道时隙长度E (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_e = []
|
||||
for i in range(4, 10): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_e.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_e'] = channel_slot_lengths_e
|
||||
|
||||
# 解析逻辑信道功能E (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_e = []
|
||||
for i in range(10, 16): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_e.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_e'] = channel_functions_e
|
||||
|
||||
res['downlink_frequency_f'] = fields[16] # 下行链路中心频率F
|
||||
res['uplink_frequency_f'] = fields[17] # 上行链路中心频率F
|
||||
|
||||
# 解析带宽F字段
|
||||
bandwidth_f = fields[18]
|
||||
res['downlink_bandwidth_f'] = (bandwidth_f >> 4) & 0x0F # 下行链路带宽F
|
||||
res['uplink_bandwidth_f'] = bandwidth_f & 0x0F # 上行链路带宽F
|
||||
|
||||
# 解析逻辑信道时隙长度F (6字节,表示12个信道,每个信道4比特)
|
||||
channel_slot_lengths_f = []
|
||||
for i in range(19, 25): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的时隙长度
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_slot_lengths_f.extend([high_nibble, low_nibble])
|
||||
res['channel_slot_lengths_f'] = channel_slot_lengths_f
|
||||
|
||||
# 解析逻辑信道功能F (6字节,表示12个信道,每个信道4比特)
|
||||
channel_functions_f = []
|
||||
for i in range(25, 31): # 6个字节
|
||||
byte_val = fields[i]
|
||||
# 高4位和低4位分别代表两个信道的功能
|
||||
high_nibble = (byte_val >> 4) & 0x0F
|
||||
low_nibble = byte_val & 0x0F
|
||||
channel_functions_f.extend([high_nibble, low_nibble])
|
||||
res['channel_functions_f'] = channel_functions_f
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_5(self, demod_data):
|
||||
"""解析类型为5的公告牌片段数据(SBB数字签名第1部分)"""
|
||||
format_str = '>B 32s' # 总共33字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:33])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['digital_signature_part1'] = fields[1] # 数字签名第1部分
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_6(self, demod_data):
|
||||
"""解析类型为6的公告牌片段数据(SBB数字签名第2部分)"""
|
||||
format_str = '>B 32s' # 总共33字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:33])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['digital_signature_part1'] = fields[1] # 数字签名第2部分
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_10(self, demod_data):
|
||||
"""解析类型为10的媒体访问控制数据"""
|
||||
format_str = '>B H B B B B B B B B H' # 总共13字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:13])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['satellite_id'] = fields[2] # 卫星ID
|
||||
res['primary_network_id'] = fields[3] # 主网络ID
|
||||
res['roaming_network_id'] = fields[4] # 漫游网络ID
|
||||
res['media_access_priority'] = fields[5] # 媒体接入优先级
|
||||
res['random_selection_interval'] = fields[6] # 随机选择间隔
|
||||
res['rac_message_access_limit'] = fields[7] # RAC消息接入限制
|
||||
res['network_status'] = fields[8] # 网络状态
|
||||
|
||||
# 解析自动重复请求/超时限制字段
|
||||
arq_timeout_field = fields[9]
|
||||
res['fragment_retry_count'] = (arq_timeout_field >> 4) & 0x0F # 4个MSB:片段重试次数
|
||||
res['timeout_timer_setting'] = arq_timeout_field & 0x0F # 4个LSB:超时定时器设置
|
||||
|
||||
res['announcement_version_number'] = fields[10] # 公告版本号
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def get_sbb_fragment_data_11(self, demod_data):
|
||||
"""解析寻呼数据(类型11)"""
|
||||
# 总共35字节 (1+2+4+4+4+4+4+4+4+4)
|
||||
format_str = '>B H I I I I I I I I'
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['vessel_station_id_1'] = fields[2] # 1号船舶电台ID
|
||||
res['vessel_station_id_2'] = fields[3] # 2号船舶电台ID
|
||||
res['vessel_station_id_3'] = fields[4] # 3号船舶电台ID
|
||||
res['vessel_station_id_4'] = fields[5] # 4号船舶电台ID
|
||||
res['vessel_station_id_5'] = fields[6] # 5号船舶电台ID
|
||||
res['vessel_station_id_6'] = fields[7] # 6号船舶电台ID
|
||||
res['vessel_station_id_7'] = fields[8] # 7号船舶电台ID
|
||||
res['vessel_station_id_8'] = fields[9] # 8号船舶电台ID
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_12(self, demod_data):
|
||||
"""解析资源分配数据(类型12)"""
|
||||
# 总共37字节 (1+2+4+1+1+1+1 + 4+1+1+1+1 + 4+1+1+1+1 + 4+1+1+1+1)
|
||||
format_str = '>B H I B B B B I B B B B I B B B B I B B B B'
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:35])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['vessel_station_id_1'] = fields[2] # 船舶电台ID 1
|
||||
res['logical_channel_1'] = fields[3] # 逻辑信道1
|
||||
res['link_id_1'] = fields[4] # 链路ID 1
|
||||
res['session_id_1'] = fields[5] # 会话ID 1
|
||||
res['uplink_cqi_1'] = fields[6] # 上行链路链路CQI 1
|
||||
res['vessel_station_id_2'] = fields[7] # 船舶电台ID 2
|
||||
res['logical_channel_2'] = fields[8] # 逻辑信道2
|
||||
res['link_id_2'] = fields[9] # 链路ID 2
|
||||
res['session_id_2'] = fields[10] # 会话ID 2
|
||||
res['uplink_cqi_2'] = fields[11] # 上行链路链路CQI 2
|
||||
res['vessel_station_id_3'] = fields[12] # 船舶电台ID 3
|
||||
res['logical_channel_3'] = fields[13] # 逻辑信道3
|
||||
res['link_id_3'] = fields[14] # 链路ID 3
|
||||
res['session_id_3'] = fields[15] # 会话ID 3
|
||||
res['uplink_cqi_3'] = fields[16] # 上行链路链路CQI 3
|
||||
res['vessel_station_id_4'] = fields[17] # 船舶电台ID 4
|
||||
res['logical_channel_4'] = fields[18] # 逻辑信道4
|
||||
res['link_id_4'] = fields[19] # 链路ID 4
|
||||
res['session_id_4'] = fields[20] # 会话ID 4
|
||||
res['uplink_cqi_4'] = fields[21] # 上行链路链路CQI 4
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_18(self, demod_data):
|
||||
"""解析送达通知数据(类型18)"""
|
||||
# 总共34字节 (1+2+1+4+1+4+1+4+1+4+1+4+1+4+1)
|
||||
format_str = '>B H B I B I B I B I B I B I B'
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:34])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['payload_size'] = fields[1] # 载荷大小
|
||||
res['satellite_id'] = fields[2] # 卫星ID
|
||||
res['vessel_station_id_1'] = fields[3] # 1号船舶电台ID
|
||||
res['session_id_1'] = fields[4] # 1号船舶会话ID
|
||||
res['vessel_station_id_2'] = fields[5] # 2号船舶电台ID
|
||||
res['session_id_2'] = fields[6] # 2号船舶会话ID
|
||||
res['vessel_station_id_3'] = fields[7] # 3号船舶电台ID
|
||||
res['session_id_3'] = fields[8] # 3号船舶会话ID
|
||||
res['vessel_station_id_4'] = fields[9] # 4号船舶电台ID
|
||||
res['session_id_4'] = fields[10] # 4号船舶会话ID
|
||||
res['vessel_station_id_5'] = fields[11] # 5号船舶电台ID
|
||||
res['session_id_5'] = fields[12] # 5号船舶会话ID
|
||||
res['vessel_station_id_6'] = fields[13] # 6号船舶电台ID
|
||||
res['session_id_6'] = fields[14] # 6号船舶会话ID
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
|
||||
def get_sbb_fragment_data_20(self, demod_data):
|
||||
"""解析资源请求数据(类型20)"""
|
||||
format_str = '>B I B B B B B' # 总共10字节
|
||||
|
||||
# 解包数据
|
||||
fields = struct.unpack(format_str, demod_data[:10])
|
||||
|
||||
# 创建结果字典
|
||||
res = {}
|
||||
res['type'] = fields[0] # 类型
|
||||
res['vessel_station_id'] = fields[1] # 船舶电台ID
|
||||
res['satellite_id'] = fields[2] # 卫星ID
|
||||
|
||||
# 解析优先级和消息长度字段
|
||||
priority_length_field = fields[3]
|
||||
res['priority'] = (priority_length_field >> 4) & 0x0F # 比特7-4,优先级
|
||||
res['message_length'] = priority_length_field & 0x0F # 比特3-0,消息长度
|
||||
|
||||
res['terminal_capability'] = fields[4] # 终端能力
|
||||
res['downlink_asc_cqi'] = fields[5] # 下行链路ASC CQI
|
||||
res['tbd'] = fields[6] # TBD
|
||||
|
||||
# 去除帧尾数据,只返回解析后的数据
|
||||
return res
|
||||
115
code/vdes_utils/vdes_message_packaged.py
Normal file
115
code/vdes_utils/vdes_message_packaged.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import struct
|
||||
import socket
|
||||
import time
|
||||
from fuadmin import settings
|
||||
|
||||
class VdesMessagePackaged:
|
||||
"""VDES消息打包工具类"""
|
||||
def __init__(self, Message, MMSI):
|
||||
self.message = Message
|
||||
self.MMSI = int(MMSI)
|
||||
self.chunk_size = 32755
|
||||
self.host = getattr(settings, 'VDES_MONITOR_HOST', 'localhost')
|
||||
self.port = getattr(settings, 'VDES_MONITOR_PORT', 8083)
|
||||
self.buffer_size = getattr(settings, 'VDES_BUFFER_SIZE', 4096)
|
||||
|
||||
|
||||
def create_tcp_packet(self, packet_type, content):
|
||||
"""创建符合VDES TCP协议的报文"""
|
||||
# 报文帧头 (4字节)
|
||||
header = b'####'
|
||||
|
||||
# 报文类型 (4字节,大端)
|
||||
type_field = struct.pack('>I', packet_type)
|
||||
|
||||
# 报文长度 (4字节,大端,内容长度)
|
||||
length = len(content)
|
||||
length_field = struct.pack('>I', length)
|
||||
|
||||
# 构建完整报文
|
||||
packet = header + type_field + length_field + content
|
||||
return packet
|
||||
|
||||
#长消息起始片段
|
||||
def long_message_start_frame(self, target_mmsi, send_priority):
|
||||
data_type = 0 #数据类型
|
||||
target_MMSI = target_mmsi #目标MMSI
|
||||
priority = send_priority #优先级
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack('>BIB', data_type, target_MMSI, priority)
|
||||
return content
|
||||
|
||||
#长消息延续片段
|
||||
def long_message_middle_frame(self, valid_binary_data):
|
||||
data_type = 1 #数据类型
|
||||
data = valid_binary_data #有效数据
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack('>B', data_type) + data
|
||||
return content
|
||||
|
||||
#长消息结束片段
|
||||
def long_message_end_frame(self):
|
||||
data_type = 2 #数据类型
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack('>B', data_type)
|
||||
return content
|
||||
|
||||
def create_message_packet(self, message, target_mmsi, send_priority):
|
||||
binary_data = message.encode('utf-8')
|
||||
message_frames = []
|
||||
if len(binary_data) < 4:
|
||||
print("使用短消息,编码后的字节数小于4字节")
|
||||
return message_frames
|
||||
else:
|
||||
print(f"使用长消息编码后的字节数为{len(binary_data)}字节")
|
||||
|
||||
chunks = [binary_data[i:i+self.chunk_size]
|
||||
for i in range(0, len(binary_data), self.chunk_size)]
|
||||
# 3. 检查总长度限制
|
||||
|
||||
if len(binary_data) > 256 * 1024:
|
||||
raise ValueError("总数据长度超过256KB限制")
|
||||
start_frame = self.long_message_start_frame(target_mmsi, send_priority)
|
||||
message_frames.append(self.create_tcp_packet(10, start_frame))
|
||||
for chunk in chunks:
|
||||
middle_frame = self.long_message_middle_frame(chunk)
|
||||
message_frames.append(self.create_tcp_packet(10, middle_frame))
|
||||
end_frame = self.long_message_end_frame()
|
||||
message_frames.append(self.create_tcp_packet(10, end_frame))
|
||||
return message_frames
|
||||
|
||||
def send_message(self):
|
||||
"""启动Socket服务端"""
|
||||
Target_MMSI = self.MMSI # 示例目标MMSI
|
||||
Send_Priority = 1 # 示例发送优先级
|
||||
message = self.message
|
||||
message_frames = self.create_message_packet(message, Target_MMSI, Send_Priority)
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
try:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.connect((self.host, self.port))
|
||||
print(f"Socket服务端已启动,监听 {self.host}:{self.port}")
|
||||
try:
|
||||
for message_frame in message_frames:
|
||||
print(f"发送报文: {message_frame.hex()}")
|
||||
sock.sendall(message_frame)
|
||||
# print("二进制表示:", ' '.join(format(byte, '08b') for byte in message_frame))
|
||||
time.sleep(0.5)
|
||||
# time.sleep(3)
|
||||
|
||||
sock.shutdown(socket.SHUT_WR)
|
||||
sock.close()
|
||||
|
||||
except (ConnectionResetError, BrokenPipeError):
|
||||
print("客户端断开连接")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n服务端已停止")
|
||||
|
||||
except Exception as e:
|
||||
print(f"服务端错误: {e}")
|
||||
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# send_message()
|
||||
Reference in New Issue
Block a user