Files
VDES_Backend/报文模拟解析/develop/send_udp.py

230 lines
8.2 KiB
Python
Raw Normal View History

2026-07-13 15:37:05 +08:00
import struct
import socket
import time
import random
SHIP_MMSIS = [random.randint(200000000, 299999999) for _ in range(10)]
YANGTZE_ESTUARY_LAT = 31.23
YANGTZE_ESTUARY_LON = 122.47
RANGE_NM = 5 # 5海里范围
def generate_random_position():
"""生成随机船只位置(基于长江口基准坐标)"""
# 1海里=1.852公里≈0.0166667度(纬度)
lat = YANGTZE_ESTUARY_LAT + (random.random() - 0.5) * RANGE_NM * 0.0166667
lon = YANGTZE_ESTUARY_LON + (random.random() - 0.5) * RANGE_NM * 0.0166667
# 转换为度分格式*10000000
longitude = int(lon * 10000000) # 东经为正数
latitude = int(lat * 10000000) # 北纬为正数
return longitude, latitude
def create_tcp_packet(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 create_gps_packet():
"""创建GPS解析数据报文(类型7)的内容"""
# 模拟GPS数据 (表11定义)
lon, lat = generate_random_position()
utc_time = 1680000000 # UTC时间 (8字节)
longitude = lon # 经度 (4字节东经120.5度)
latitude = lat # 纬度 (4字节北纬30.5度)
altitude = 50 # 海拔高度 (4字节50米)
used_sats = 10 # 定位使用的卫星数 (2字节)
gps_sats = 8 # GPS卫星数 (2字节)
beidou_sats = 7 # 北斗卫星数 (2字节)
gps_status = 1 # GPS状态 (1字节单点定位)
position_mode = 3 # 定位模式 (1字节3D定位)
hdop = 2550 # 水平精度因子 (4字节25.5米)
speed = 1024 # 运动速度 (4字节10.24节)
direction = 359 # 运动方向 (4字节359度)
# 打包为二进制 (大端字节序)
content = struct.pack(
'>QiiiHHHBBIII', # 格式: Q(8),i(4),i(4),i(4),H(2),H(2),H(2),B(1),B(1),I(4),I(4),I(4)
utc_time,
longitude,
latitude,
altitude,
used_sats,
gps_sats,
beidou_sats,
gps_status,
position_mode,
hdop,
speed,
direction
)
return content
def create_ais_info_packet(demod_content):
"""创建解调信息报文(类型12)的内容"""
# 模拟解调信息数据
message_type = 12 # 消息类型 (1字节)
satellite_id = 1 # 卫星ID (1字节)
demodulator_id = 0 # 解调器编号 (1字节)
demod_slot = 1234 # 解调时隙 (2字节)
receive_delay = 5678 # 接收延迟 (2字节单位us)
snr = 25.75 # 信噪比 (2字节dB)
# 处理信噪比 (高8位整数部分低8位小数部分)
snr_int = int(snr)
snr_frac = int((snr - snr_int) * 100) # 转换为0-99的小数部分
snr_packed = (snr_int & 0xFF) << 8 | (snr_frac & 0xFF)
# 打包为二进制 (大端字节序)
content = struct.pack(
'>BBBHHH', # 格式: B(1),B(1),B(1),H(2),H(2),h(2)
message_type,
satellite_id,
demodulator_id,
demod_slot,
receive_delay,
snr_packed
)
return content + demod_content
def encode_demod_fields():
"""编码解调信息字段并打印解码结果"""
# 初始化二进制位列表
bits = []
# 示例数据
message_id = 1 # 消息ID (6 bits)
repeat_indicator = 0 # 转发指示符 (2 bits)
user_id = random.choice(SHIP_MMSIS) # 用户ID/MMSI (30 bits)
navigation_status = 8 # 导航状态 (4 bits) - 航行中
rot = -128 # 旋转速率 (8 bits) - 无信息
sog = 102 # 地面航速 (10 bits) - 10.2节
position_accuracy = 1 # 位置准确度 (1 bit) - 高
longitude = 124.5 + (random.random() - 0.5) * RANGE_NM * 0.0166667 # 经度 (28 bits) - 东经120.5°
latitude = 30.5 + (random.random() - 0.5) * RANGE_NM * 0.0166667 # 纬度 (27 bits) - 北纬30.5°
cog = 900 # 地面航线 (12 bits) - 90.0°
true_heading = 90 # 实际航向 (9 bits) - 90°
timestamp = 30 # 时戳 (6 bits) - UTC秒
special_maneuver = 0 # 特定操纵指示符 (2 bits)
spare = 0 # 备用 (3 bits)
raim_flag = 0 # RAIM标志 (1 bit)
comm_state = 0 # 通信状态 (19 bits)
# 添加各个字段(按照协议顺序)
bits.append(format(message_id, '06b')) # 消息ID
bits.append(format(repeat_indicator, '02b')) # 转发指示符
bits.append(format(user_id, '030b')) # 用户ID
bits.append(format(navigation_status, '04b')) # 导航状态
# 旋转速率 (8位有符号整数)
rot_bits = rot & 0xFF
bits.append(format(rot_bits, '08b'))
# 地面航速 (10位)
bits.append(format(min(sog, 1023), '010b'))
# 位置准确度 (1位)
bits.append(str(position_accuracy))
# 经度 (28位)
# 转换为1/10,000分钟单位
longitude_min = int(longitude * 60 * 10000)
bits.append(format(longitude_min & 0x0FFFFFFF, '028b'))
# 纬度 (27位)
latitude_min = int(latitude * 60 * 10000)
bits.append(format(latitude_min & 0x07FFFFFF, '027b'))
# 地面航线 (12位)
bits.append(format(min(cog, 3600), '012b'))
# 实际航向 (9位)
bits.append(format(min(true_heading, 511), '09b'))
# 时戳 (6位)
bits.append(format(min(timestamp, 63), '06b'))
# 特定操纵指示符 (2位)
bits.append(format(special_maneuver, '02b'))
# 备用 (3位)
bits.append(format(spare, '03b'))
# RAIM标志 (1位)
bits.append(str(raim_flag))
# 通信状态 (19位)
bits.append(format(comm_state, '019b'))
# 合并所有位
bit_string = ''.join(bits)
# 验证总位数
total_bits = len(bit_string)
if total_bits != 168:
print(f"警告: 总位数应为168实际为{total_bits}")
# 将位字符串转换为字节
binary_int = int(bit_string, 2) # 将二进制字符串转换为整数
byte_length = (len(bit_string) + 7) // 8 # 计算需要的字节数
binary_data = binary_int.to_bytes(byte_length, 'big') # 转换为字节
return binary_data
def print_binary(data):
"""打印二进制数据"""
print("十六进制表示:", data.hex())
print("二进制表示:", ' '.join(format(byte, '08b') for byte in data))
def send_data_continuously():
"""持续发送AIS和GPS数据到指定端口"""
host = 'localhost'
port = 8082
# 无连接型
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
print(f"模拟发送数据到 {host}:{port} (UDP)")
while True:
try:
# 生成并发送AIS报文
demod_content = encode_demod_fields()
ais_data = create_ais_info_packet(demod_content)
ais_packet = create_tcp_packet(12, ais_data)
s.sendto(ais_packet, (host, port))
print("AIS报文:")
print_binary(ais_packet)
print("已发送AIS报文")
# 生成并发送GPS报文
gps_content = create_gps_packet()
gps_packet = create_tcp_packet(7, gps_content)
s.sendto(gps_packet, (host, port))
print("\nGPS报文:")
print_binary(gps_packet)
print("已发送GPS报文")
# 等待1秒
time.sleep(3)
except KeyboardInterrupt:
print("\n停止发送数据")
break
except Exception as e:
print(f"发送数据时出错: {e}")
break
if __name__ == "__main__":
send_data_continuously()