first commit
This commit is contained in:
214
报文模拟解析/develop/vdes_send.py
Normal file
214
报文模拟解析/develop/vdes_send.py
Normal file
@@ -0,0 +1,214 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user