253 lines
10 KiB
Python
253 lines
10 KiB
Python
|
|
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()
|