Files
VDES_Backend/报文模拟解析/地检设备模拟/local_inspection.py
2026-07-13 15:37:05 +08:00

395 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import struct
import socket
import time
import random
from random import uniform, randint
import math
import threading
from vdes_message_unpack import VdesMessageUnpack
vdes_message_queue = []
message_queue_lock = threading.Lock()
SHIP_MMSIS = [random.randint(336295939, 336295969) for _ in range(10)]
SHIP_STATUS = {}
# 生成包含30艘船信息的列表MMSI、纬度、经度、航向
SHIP_LIST = [
(
336295939 + i, # MMSI号
round(random.uniform(30.0, 34.0), 6),
round(random.uniform(123.5, 126.0), 6),
random.randint(0, 359)
) for i in range(30)
]
# 初始化船只状态
for ship in SHIP_LIST:
mmsi, init_lat, init_lon, heading = ship
SHIP_STATUS[mmsi] = {
'current_lat': init_lat,
'current_lon': init_lon,
'heading': heading,
'speed': 280.24 #加速100倍用于演示
}
YANGTZE_ESTUARY_LAT = 31.23
YANGTZE_ESTUARY_LON = 123.87
RANGE_NM = 5 # 5海里范围
current_lat = YANGTZE_ESTUARY_LAT # 当前纬度
current_lon = YANGTZE_ESTUARY_LON # 当前经度
own_heading = 90 # 固定航向 (50度)
def generate_random_position():
"""生成船只位置(基于当前位置和航向计算)"""
global current_lat, current_lon, own_heading
# 速度为10.24节(来自GPS报文设置)
speed_knots = 300.24 #加速100倍用于演示
# 转换为公里/小时 (1节 = 1.852公里/小时)
speed_kmh = speed_knots * 1.852
# 转换为公里/秒 (发送间隔为1秒)
speed_kmps = speed_kmh / 3600
# 计算1秒内移动的距离(公里)
distance_km = speed_kmps * 1
# 将航向转换为弧度
heading_rad = math.radians(own_heading)
# 计算纬度和经度变化
# 1度纬度 ≈ 111公里
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
# 1度经度 ≈ 111公里 * cos(纬度)
delta_lon = distance_km / (111.0 * math.cos(math.radians(current_lat))) * math.sin(heading_rad)
# 更新当前位置
current_lat += delta_lat
current_lon += delta_lon
# 转换为度分格式*10^7
longitude = int(current_lon * 10000000)
latitude = int(current_lat * 10000000)
return longitude, latitude
def generate_ship_position(mmsi):
"""根据船只MMSI生成当前位置"""
global SHIP_STATUS
ship = SHIP_STATUS[mmsi] #获取全局船只中的一个进行修改
speed_knots = ship['speed']
speed_kmh = speed_knots * 1.852
speed_kmps = speed_kmh / 3600
distance_km = speed_kmps * 1
# 航向转弧度
heading_rad = math.radians(ship['heading'])
# 计算经纬度变化
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
delta_lon = distance_km / (111.0 * math.cos(math.radians(ship['current_lat']))) * math.sin(heading_rad)
# 更新船只状态
ship['current_lat'] += delta_lat
ship['current_lon'] += delta_lon
# 返回新位置
return ship['current_lon'], ship['current_lat']
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 = own_heading # 运动方向 (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_vdes_info_packet(demod_content, Demodulator_id):
"""创建解调信息报文(类型12)的内容"""
# 模拟解调信息数据
message_type = 12 # 消息类型 (1字节)
satellite_id = 1 # 卫星ID (1字节)
demodulator_id = Demodulator_id # 解调器编号 (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_ais_demod_fields(SHIP_MMSI, lat, lon, heading):
"""编码AIS解调信息字段"""
# 初始化二进制位列表
bits = []
# 示例数据
message_id = 1 # 消息ID (6 bits)
repeat_indicator = 0 # 转发指示符 (2 bits)
user_id = SHIP_MMSI # 用户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, latitude = lon, lat # 经纬度
cog = heading # 地面航线 (12 bits) - 90.0°
true_heading = heading # 实际航向 (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 encode_vdes_demod_fields(Type, Fragment_num, MMSI, Payload):
"""编码VDES解调信息字段"""
source_id = 12345678 # 信源电台ID
satellite_id = 1 # 卫星ID
session_id = 100 # 会话ID
dest_id = MMSI # 目的地电台ID
fragment_num = Fragment_num # 片段编号
binary_payload = Payload # 载荷内容
# 计算载荷大小 (字段3到8的大小)
payload_size = 4 + 1 + 1 + 4 + 2 + len(binary_payload)
# 打包为二进制 (大端字节序)
content = struct.pack(
'>B H I B B I H', # 格式: B(1),H(2),I(4),B(1),B(1),I(4),H(2)
Type, # 类型 (1字节)
payload_size, # 载荷大小 (2字节)
source_id, # 信源电台ID (4字节)
satellite_id, # 卫星ID (1字节)
session_id, # 会话ID (1字节)
dest_id, # 目的地电台ID (4字节)
fragment_num # 片段编号 (2字节)
)
return content + binary_payload # 添加载荷内容
def print_binary(data):
"""打印二进制数据"""
print("十六进制表示:", data.hex())
print("二进制表示:", ' '.join(format(byte, '08b') for byte in data))
def handle_client(conn, addr):
"""处理客户端连接的线程函数"""
print(f"客户端连接来自: {addr}")
unpacker = VdesMessageUnpack()
mmsi: int
fragment_num = 0
# 创建发送线程
send_thread = threading.Thread(target=send_messages, args=(conn,))
send_thread.daemon = True
send_thread.start()
# 接收消息
while True:
data = conn.recv(65536)
if data:
# print(f"收到VDES客户端消息: {data.hex()}")
try:
frame_type, message = unpacker.unpack_tcp_packet(data)
mmsi = unpacker.get_mmsi()
if frame_type == 0: # 起始片段
Type = 30
elif frame_type == 1: # 延续片段
Type = 31
elif frame_type == 2: # 结束片段
Type = 32
vdes_demod_content = encode_vdes_demod_fields(Type, fragment_num, mmsi, message)
vdes_data = create_ais_vdes_info_packet(vdes_demod_content, 0)
vdes_packet = create_tcp_packet(12, vdes_data)
fragment_num += 1
# 将消息添加到全局队列
# with message_queue_lock:
vdes_message_queue.append(vdes_packet)
if not unpacker.get_unpack_statu():
fragment_num = 0
# print("解析消息完成")
except ValueError as e:
print(f"报文解析错误: {e}")
def send_messages(conn):
"""持续发送消息的线程函数"""
try:
while True:
# 检查并发送队列中的VDES消息
# with message_queue_lock:
# print(11111111111111111111111)
# print(vdes_message_queue)
if vdes_message_queue:
print(vdes_message_queue)
packet = vdes_message_queue.pop(0)
conn.sendall(packet)
print(f"发送VDES报文: {packet.hex()}")
time.sleep(0.5) # 模拟发送间隔
# 生成并发送AIS报文
# selected_ship = random.choice(SHIP_LIST)
# mmsi = selected_ship[0]
# lon, lat = generate_ship_position(mmsi)
# heading = SHIP_STATUS[mmsi]['heading']
# ais_demod_content = encode_ais_demod_fields(mmsi, lat, lon, heading)
# ais_data = create_ais_vdes_info_packet(ais_demod_content, 4)
# ais_packet = create_tcp_packet(12, ais_data)
# print(f"发送AIS报文: {ais_packet.hex()}")
# conn.sendall(ais_packet)
# # 生成并发送GPS报文
# gps_content = create_gps_packet()
# gps_packet = create_tcp_packet(7, gps_content)
# conn.sendall(gps_packet)
# time.sleep(2)
except (ConnectionResetError, BrokenPipeError):
print("客户端断开连接")
def send_data_continuously():
"""启动Socket服务端"""
host = '127.0.0.1'
port = 10
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
print(f"Socket服务端已启动监听 {host}:{port}")
try:
while True:
conn, addr = s.accept()
client_thread = threading.Thread(target=handle_client, args=(conn, addr))
client_thread.daemon = True
client_thread.start()
except KeyboardInterrupt:
print("\n服务端已停止")
except Exception as e:
print(f"服务端错误: {e}")
if __name__ == "__main__":
send_data_continuously()