Files

253 lines
10 KiB
Python
Raw Permalink Normal View History

2026-07-13 15:37:05 +08:00
import socket
import struct
import random
import time
import threading
from datetime import datetime, timedelta
# 报文类型常量
MSG_DEBUG = 0
MSG_LVDS = 1
MSG_CAN = 2
MSG_AD9361_CTRL = 3
MSG_WAVE_CTRL = 4
MSG_TEST_PARAM_CTRL = 5
MSG_TEST_WAVE_UPLOAD = 6
MSG_GPS = 7
MSG_STATUS_MONITOR = 8
MSG_PROTO_PARAM_CTRL = 9
MSG_DATA_UPLOAD = 10
MSG_DATA_COLLECT = 11
MSG_DEMOD_DATA = 12
MSG_NOTIFICATION = 13
# 解调器编号常量
VDES_DEMOD_RANGE = (0, 3) # VDES解调器编号0-3
AIS_DEMOD_RANGE = (4, 5) # AIS解调器编号4-5
# 长江入海口坐标范围 (31.0°N - 31.5°N, 121.5°E - 122.0°E)
YANGTZE_DELTA_CENTER = (31.23, 121.77) # 上海附近
DELTA_RANGE = 0.05 # ±0.05度 ≈ 5海里
class VDESServer:
def __init__(self, host='localhost', port=10):
self.host = host
self.port = port
self.header = b'####'
self.running = False
self.server_socket = None
self.ship_mmsi = 123456789 # 船舶MMSI
def start(self):
"""启动TCP服务器"""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(1)
self.running = True
print(f"VDES Server started on {self.host}:{self.port}")
# 接受客户端连接
client_socket, addr = self.server_socket.accept()
print(f"Connected by {addr}")
# 启动接收线程和定时发送线程
recv_thread = threading.Thread(target=self.handle_client, args=(client_socket,))
send_thread = threading.Thread(target=self.send_periodic_messages, args=(client_socket,))
recv_thread.start()
send_thread.start()
recv_thread.join()
send_thread.join()
def stop(self):
"""停止服务器"""
self.running = False
if self.server_socket:
self.server_socket.close()
def build_packet(self, msg_type, content):
"""构建协议报文:帧头(4) + 类型(4) + 长度(4) + 内容(N)"""
length = len(content)
return self.header + struct.pack('>I', msg_type) + struct.pack('>I', length) + content
def parse_packet(self, data):
"""解析接收到的报文"""
if len(data) < 12 or data[:4] != self.header:
return None, None
msg_type = struct.unpack('>I', data[4:8])[0]
length = struct.unpack('>I', data[8:12])[0]
content = data[12:12+length] if len(data) >= 12 + length else None
return msg_type, content
def handle_debug_command(self, content):
"""处理调试指令报文类型0"""
cmd_type = content[0] if content else 0
responses = {
0: "初始化地检",
1: "测试波形上注完成",
2: "LVDS数据开始上注",
3: "CAN初始化",
4: "LVDS初始化",
5: "AD9361重新校准",
6: "GPS初始化"
}
response_msg = responses.get(cmd_type, "未知指令")
print(f"执行调试指令: {response_msg}")
# 返回通知报文确认
return self.build_packet(MSG_NOTIFICATION, struct.pack('B', 0)) # 类型0: VDES协议开始执行
def handle_client(self, client_socket):
"""处理客户端请求"""
while self.running:
try:
data = client_socket.recv(32768)
if not data:
break
msg_type, content = self.parse_packet(data)
if msg_type is None:
continue
# 根据报文类型处理
if msg_type == MSG_DEBUG:
response = self.handle_debug_command(content)
elif msg_type == MSG_AD9361_CTRL:
response = self.build_packet(MSG_NOTIFICATION, struct.pack('B', 7)) # AD9361状态更新
else:
response = self.build_packet(MSG_NOTIFICATION, struct.pack('B', 3)) # 通用确认
client_socket.send(response)
except Exception as e:
print(f"处理错误: {e}")
break
def send_periodic_messages(self, client_socket):
"""定时发送状态监控和GPS报文"""
while self.running:
try:
# 每秒发送GPS数据类型7
gps_data = self.generate_gps_data()
client_socket.send(self.build_packet(MSG_GPS, gps_data))
# 每秒发送状态监控类型8
status_data = bytes([random.randint(0, 100) for _ in range(50)]) # 随机状态数据
client_socket.send(self.build_packet(MSG_STATUS_MONITOR, status_data))
# 每5秒发送解调数据类型12
if int(time.time()) % 5 == 0:
demod_data = self.generate_demod_data()
client_socket.send(self.build_packet(MSG_DEMOD_DATA, demod_data))
time.sleep(1)
except Exception as e:
print(f"发送错误: {e}")
break
def generate_gps_data(self):
"""生成长江入海口附近的GPS数据"""
# 在长江入海口附近随机生成位置
lat = YANGTZE_DELTA_CENTER[0] + random.uniform(-DELTA_RANGE, DELTA_RANGE)
lon = YANGTZE_DELTA_CENTER[1] + random.uniform(-DELTA_RANGE, DELTA_RANGE)
# 转换为度分格式*10000000
lat_int = int(lat * 10000000)
lon_int = int(lon * 10000000)
# 其他GPS数据字段
utc_time = int((datetime.utcnow() - datetime(1980, 1, 6)).total_seconds())
altitude = random.randint(-10, 50) # 海拔高度(米)
used_satellites = random.randint(5, 12)
gps_satellites = random.randint(4, 8)
beidou_satellites = random.randint(4, 8)
gps_status = random.randint(1, 4) # GPS状态
pos_mode = random.randint(1, 3) # 定位模式
hdop = random.randint(10, 50) # 水平精度因子
speed = random.randint(0, 2000) # 运动速度 (0.01节)
course = random.randint(0, 35900) # 运动方向 (度*100)
# 打包GPS数据
return struct.pack('>Q', utc_time) + \
struct.pack('>i', lon_int) + \
struct.pack('>i', lat_int) + \
struct.pack('>i', altitude) + \
struct.pack('>H', used_satellites) + \
struct.pack('>H', gps_satellites) + \
struct.pack('>H', beidou_satellites) + \
struct.pack('B', gps_status) + \
struct.pack('B', pos_mode) + \
struct.pack('>I', hdop) + \
struct.pack('>I', speed) + \
struct.pack('>I', course)
def generate_demod_data(self):
"""生成解调数据报文类型12"""
demodulator_id = random.randint(0, 5)
msg_type = 12 # 固定值12
satellite_id = random.randint(1, 255)
demod_data = struct.pack('B', msg_type) + struct.pack('B', satellite_id) + struct.pack('B', demodulator_id)
# 根据解调器编号生成数据
if VDES_DEMOD_RANGE[0] <= demodulator_id <= VDES_DEMOD_RANGE[1]:
# VDES解调器 (0-3): 使用M.2092-1 Annex5格式
msg_id = random.choice([10, 11, 12, 20, 21])
content = struct.pack('B', msg_id) + os.urandom(20) # 随机生成20字节数据
else:
# AIS解调器 (4-5): 使用M.1371-5 Annex8 Message1格式
content = self.generate_ais_message1()
return demod_data + content
def generate_ais_message1(self):
"""生成AIS Message 1位置报告"""
# 在长江入海口附近随机生成位置
lat = YANGTZE_DELTA_CENTER[0] + random.uniform(-DELTA_RANGE, DELTA_RANGE)
lon = YANGTZE_DELTA_CENTER[1] + random.uniform(-DELTA_RANGE, DELTA_RANGE)
# 转换为1/10000分钟格式
lat_min = int(lat * 60 * 10000)
lon_min = int(lon * 60 * 10000)
# 其他AIS字段
message_id = 1
repeat_indicator = 0
navigational_status = random.randint(0, 15) # 航行状态
rate_of_turn = random.randint(-128, 127) # 转向率
speed_over_ground = random.randint(0, 1022) # 对地航速 (0.1节)
position_accuracy = 1 # 高精度
course_over_ground = random.randint(0, 3599) # 对地航向 (0.1度)
true_heading = random.randint(0, 359) # 真航向
timestamp = random.randint(0, 59) # UTC秒
maneuver_indicator = random.randint(0, 1) # 操纵指示器
spare = 0
raim_flag = 0 # RAIM标志
# 打包AIS Message 1
return struct.pack('>B', message_id) + \
struct.pack('>B', (repeat_indicator << 6) | (navigational_status << 2) | (rate_of_turn >> 6)) + \
struct.pack('>B', (rate_of_turn & 0x3F) << 2) | (speed_over_ground >> 8) + \
struct.pack('>B', speed_over_ground & 0xFF) + \
struct.pack('>B', (position_accuracy << 7) | (lon_min >> 25)) + \
struct.pack('>B', (lon_min >> 17) & 0xFF) + \
struct.pack('>B', (lon_min >> 9) & 0xFF) + \
struct.pack('>B', (lon_min >> 1) & 0xFF) + \
struct.pack('>B', ((lon_min & 0x1) << 7) | (lat_min >> 20)) + \
struct.pack('>B', (lat_min >> 12) & 0xFF) + \
struct.pack('>B', (lat_min >> 4) & 0xFF) + \
struct.pack('>B', ((lat_min & 0xF) << 4) | (course_over_ground >> 8)) + \
struct.pack('>B', course_over_ground & 0xFF) + \
struct.pack('>H', true_heading) + \
struct.pack('>B', timestamp) + \
struct.pack('>B', (maneuver_indicator << 2) | spare) + \
struct.pack('>B', raim_flag)
if __name__ == "__main__":
server = VDESServer()
try:
server.start()
except PermissionError:
print("错误需要管理员权限绑定到端口10")
except KeyboardInterrupt:
server.stop()