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() |