214 lines
7.2 KiB
Python
214 lines
7.2 KiB
Python
|
|
import struct
|
|||
|
|
import socket
|
|||
|
|
import time
|
|||
|
|
import random
|
|||
|
|
import math
|
|||
|
|
|
|||
|
|
class Ship:
|
|||
|
|
def __init__(self, mmsi, lat, lon, heading, speed=280.24):
|
|||
|
|
self.mmsi = mmsi
|
|||
|
|
self.lat = lat
|
|||
|
|
self.lon = lon
|
|||
|
|
self.heading = heading
|
|||
|
|
self.speed = speed # 加速100倍用于演示
|
|||
|
|
|
|||
|
|
def update_position(self):
|
|||
|
|
"""更新船只位置"""
|
|||
|
|
# 速度转换
|
|||
|
|
speed_kmh = self.speed * 1.852
|
|||
|
|
speed_kmps = speed_kmh / 3600
|
|||
|
|
distance_km = speed_kmps * 1
|
|||
|
|
|
|||
|
|
# 航向转弧度
|
|||
|
|
heading_rad = math.radians(self.heading)
|
|||
|
|
|
|||
|
|
# 计算经纬度变化
|
|||
|
|
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
|
|||
|
|
delta_lon = distance_km / (111.0 * math.cos(math.radians(self.lat))) * math.sin(heading_rad)
|
|||
|
|
|
|||
|
|
# 更新位置
|
|||
|
|
self.lat += delta_lat
|
|||
|
|
self.lon += delta_lon
|
|||
|
|
|
|||
|
|
return self.lon, self.lat
|
|||
|
|
|
|||
|
|
class OwnShip(Ship):
|
|||
|
|
def __init__(self, lat, lon, heading, speed=280.24):
|
|||
|
|
super().__init__(336295999, lat, lon, heading, speed) # 使用固定MMSI作为本船
|
|||
|
|
|
|||
|
|
def get_position(self):
|
|||
|
|
"""获取本船位置并更新"""
|
|||
|
|
return self.update_position()
|
|||
|
|
|
|||
|
|
class VDESPacketCreator:
|
|||
|
|
@staticmethod
|
|||
|
|
def create_tcp_packet(packet_type, content):
|
|||
|
|
"""创建符合VDES TCP协议的报文"""
|
|||
|
|
header = b'####'
|
|||
|
|
type_field = struct.pack('>I', packet_type)
|
|||
|
|
length_field = struct.pack('>I', len(content))
|
|||
|
|
return header + type_field + length_field + content
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_gps_packet(cls, own_ship):
|
|||
|
|
"""创建GPS解析数据报文(类型7)的内容"""
|
|||
|
|
lon, lat = own_ship.get_position()
|
|||
|
|
|
|||
|
|
# 打包GPS数据
|
|||
|
|
return struct.pack(
|
|||
|
|
'>QiiiHHHBBIII',
|
|||
|
|
1680000000, # UTC时间
|
|||
|
|
int(lon * 10000000), # 经度
|
|||
|
|
int(lat * 10000000), # 纬度
|
|||
|
|
50, # 海拔高度
|
|||
|
|
10, # 定位使用的卫星数
|
|||
|
|
8, # GPS卫星数
|
|||
|
|
7, # 北斗卫星数
|
|||
|
|
1, # GPS状态
|
|||
|
|
3, # 定位模式
|
|||
|
|
2550, # 水平精度因子
|
|||
|
|
1024, # 运动速度
|
|||
|
|
own_ship.heading # 运动方向
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create_ais_info_packet(cls, ship):
|
|||
|
|
"""创建解调信息报文(类型12)的内容"""
|
|||
|
|
demod_content = cls.encode_demod_fields(ship)
|
|||
|
|
|
|||
|
|
# 打包解调信息
|
|||
|
|
message_type = 12
|
|||
|
|
satellite_id = 1
|
|||
|
|
demodulator_id = 0
|
|||
|
|
demod_slot = 1234
|
|||
|
|
receive_delay = 5678
|
|||
|
|
snr = 25.75
|
|||
|
|
|
|||
|
|
# 处理信噪比
|
|||
|
|
snr_int = int(snr)
|
|||
|
|
snr_frac = int((snr - snr_int) * 100)
|
|||
|
|
snr_packed = (snr_int & 0xFF) << 8 | (snr_frac & 0xFF)
|
|||
|
|
|
|||
|
|
header = struct.pack(
|
|||
|
|
'>BBBHHH',
|
|||
|
|
message_type,
|
|||
|
|
satellite_id,
|
|||
|
|
demodulator_id,
|
|||
|
|
demod_slot,
|
|||
|
|
receive_delay,
|
|||
|
|
snr_packed
|
|||
|
|
)
|
|||
|
|
return header + demod_content
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def encode_demod_fields(ship):
|
|||
|
|
"""编码解调信息字段"""
|
|||
|
|
lon, lat = ship.update_position()
|
|||
|
|
|
|||
|
|
# 准备数据
|
|||
|
|
message_id = 1
|
|||
|
|
repeat_indicator = 0
|
|||
|
|
user_id = ship.mmsi
|
|||
|
|
navigation_status = 8
|
|||
|
|
rot = -128
|
|||
|
|
sog = 102
|
|||
|
|
position_accuracy = 1
|
|||
|
|
cog = ship.heading
|
|||
|
|
true_heading = ship.heading
|
|||
|
|
timestamp = 30
|
|||
|
|
special_maneuver = 0
|
|||
|
|
spare = 0
|
|||
|
|
raim_flag = 0
|
|||
|
|
comm_state = 0
|
|||
|
|
|
|||
|
|
# 转换为协议要求的格式
|
|||
|
|
longitude_min = int(lon * 60 * 10000)
|
|||
|
|
latitude_min = int(lat * 60 * 10000)
|
|||
|
|
|
|||
|
|
# 构建二进制位
|
|||
|
|
bits = [
|
|||
|
|
format(message_id, '06b'),
|
|||
|
|
format(repeat_indicator, '02b'),
|
|||
|
|
format(user_id, '030b'),
|
|||
|
|
format(navigation_status, '04b'),
|
|||
|
|
format(rot & 0xFF, '08b'),
|
|||
|
|
format(min(sog, 1023), '010b'),
|
|||
|
|
str(position_accuracy),
|
|||
|
|
format(longitude_min & 0x0FFFFFFF, '028b'),
|
|||
|
|
format(latitude_min & 0x07FFFFFF, '027b'),
|
|||
|
|
format(min(cog, 3600), '012b'),
|
|||
|
|
format(min(true_heading, 511), '09b'),
|
|||
|
|
format(min(timestamp, 63), '06b'),
|
|||
|
|
format(special_maneuver, '02b'),
|
|||
|
|
format(spare, '03b'),
|
|||
|
|
str(raim_flag),
|
|||
|
|
format(comm_state, '019b')
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# 合并所有位并转换为字节
|
|||
|
|
bit_string = ''.join(bits)
|
|||
|
|
binary_int = int(bit_string, 2)
|
|||
|
|
byte_length = (len(bit_string) + 7) // 8
|
|||
|
|
return binary_int.to_bytes(byte_length, 'big')
|
|||
|
|
|
|||
|
|
class VDEServer:
|
|||
|
|
def __init__(self, host='127.0.0.1', port=10):
|
|||
|
|
self.host = host
|
|||
|
|
self.port = port
|
|||
|
|
self.own_ship = OwnShip(31.23, 123.87, 90)
|
|||
|
|
self.ships = self.create_ships(30)
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def create_ships(count):
|
|||
|
|
"""创建指定数量的船只"""
|
|||
|
|
return [
|
|||
|
|
Ship(
|
|||
|
|
mmsi=336295939 + i,
|
|||
|
|
lat=round(random.uniform(30.0, 34.0), 6),
|
|||
|
|
lon=round(random.uniform(123.5, 126.0), 6),
|
|||
|
|
heading=random.randint(0, 359)
|
|||
|
|
) for i in range(count)
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
def send_data_continuously(self):
|
|||
|
|
"""启动Socket服务端并持续发送数据"""
|
|||
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|||
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|||
|
|
s.bind((self.host, self.port))
|
|||
|
|
s.listen(1)
|
|||
|
|
print(f"Socket服务端已启动,监听 {self.host}:{self.port}")
|
|||
|
|
|
|||
|
|
while True:
|
|||
|
|
try:
|
|||
|
|
conn, addr = s.accept()
|
|||
|
|
print(f"客户端连接来自: {addr}")
|
|||
|
|
|
|||
|
|
while True:
|
|||
|
|
try:
|
|||
|
|
# 随机选择一艘船发送AIS报文
|
|||
|
|
selected_ship = random.choice(self.ships)
|
|||
|
|
ais_data = VDESPacketCreator.create_ais_info_packet(selected_ship)
|
|||
|
|
ais_packet = VDESPacketCreator.create_tcp_packet(12, ais_data)
|
|||
|
|
conn.sendall(ais_packet)
|
|||
|
|
|
|||
|
|
# 发送本船GPS报文
|
|||
|
|
gps_content = VDESPacketCreator.create_gps_packet(self.own_ship)
|
|||
|
|
gps_packet = VDESPacketCreator.create_tcp_packet(7, gps_content)
|
|||
|
|
conn.sendall(gps_packet)
|
|||
|
|
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
|
|||
|
|
except (ConnectionResetError, BrokenPipeError):
|
|||
|
|
print("客户端断开连接")
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
except KeyboardInterrupt:
|
|||
|
|
print("\n服务端已停止")
|
|||
|
|
break
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"服务端错误: {e}")
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
server = VDEServer()
|
|||
|
|
server.send_data_continuously()
|