first commit
This commit is contained in:
134
报文模拟解析/develop/receive.py
Normal file
134
报文模拟解析/develop/receive.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import socket
|
||||
import struct
|
||||
def get_tcp_data(packet_data):
|
||||
#判断帧头是否符合卓研
|
||||
if packet_data[:4].decode('ascii') == '####':
|
||||
print("开始解析报文帧")
|
||||
packet_type = struct.unpack('>I', packet_data[4:8])[0] #获取报文类型 7为gps, 12为ais
|
||||
packet_lenth = struct.unpack('>I', packet_data[8:12])[0]
|
||||
packet_content = packet_data[12:]
|
||||
if packet_type == 7:
|
||||
print(f"GPS内容长度: {packet_lenth} 字节")
|
||||
data = get_gps_data(packet_content)
|
||||
print(data)
|
||||
elif packet_type == 12:
|
||||
print(f"AIS内容长度: {packet_lenth} 字节")
|
||||
data = get_ais_data(packet_content)
|
||||
print(data)
|
||||
|
||||
def get_gps_data(packet_content):
|
||||
fields = struct.unpack('>QiiiHHHBBIII', packet_content)
|
||||
res = {}
|
||||
res['utc_time'] = fields[0] # UTC时间 (8字节)
|
||||
res['longitude'] = fields[1]/10000000 # 经度 (4字节,东经120.5度)
|
||||
res['latitude'] = fields[2]/10000000 # 纬度 (4字节,北纬30.5度)
|
||||
res['altitude'] = fields[3] # 海拔高度 (4字节,50米)
|
||||
res['used_sats'] = fields[4] # 定位使用的卫星数 (2字节)
|
||||
res['gps_sats'] = fields[5] # GPS卫星数 (2字节)
|
||||
res['beidou_sats'] = fields[6] # 北斗卫星数 (2字节)
|
||||
res['gps_status'] = fields[7] # GPS状态 (1字节,单点定位)
|
||||
res['position_mode'] = fields[8] # 定位模式 (1字节,3D定位)
|
||||
res['hdop'] = fields[9]/10 # 水平精度因子 (4字节,25.5米)
|
||||
res['speed'] = fields[10]/100 # 运动速度 (4字节,10.24节)
|
||||
res['direction'] = fields[11] # 运动方向 (4字节,359度)
|
||||
return res
|
||||
|
||||
def get_ais_data(packet_content):
|
||||
res ={}
|
||||
demod_packet = packet_content[:9]
|
||||
demod_data = packet_content[9:]
|
||||
fields = struct.unpack('>BBBHHH', demod_packet)
|
||||
res['message_type'] = fields[0]
|
||||
res['satellite_id'] = fields[1]
|
||||
res['demodulator_id'] = fields[2]
|
||||
res['demod_slot'] = fields[3]
|
||||
res['receive_delay'] = fields[4]
|
||||
snr_packed = fields[5]
|
||||
# 解析SNR
|
||||
snr_int = (snr_packed >> 8) & 0xFF
|
||||
snr_frac = snr_packed & 0xFF
|
||||
res['snr'] = snr_int + snr_frac / 100.0
|
||||
print(demod_data)
|
||||
res['demod_data'] = get_demod_data(demod_data)
|
||||
return res
|
||||
|
||||
def get_demod_data(bit_string):
|
||||
# 如果输入是字节类型,先转换为二进制字符串
|
||||
if isinstance(bit_string, bytes):
|
||||
bit_string = ''.join(format(byte, '08b') for byte in bit_string)
|
||||
pos = 0
|
||||
decoded = {}
|
||||
|
||||
decoded['message_id'] = int(bit_string[pos:pos+6], 2) # 消息ID (6 bits)
|
||||
pos += 6
|
||||
decoded['repeat_indicator'] = int(bit_string[pos:pos+2], 2) # 转发指示符 (2 bits)
|
||||
pos += 2
|
||||
decoded['user_id'] = int(bit_string[pos:pos+30], 2) # 用户ID/MMSI (30 bits)
|
||||
pos += 30
|
||||
decoded['navigation_status'] = int(bit_string[pos:pos+4], 2) # 导航状态 (4 bits)
|
||||
pos += 4
|
||||
|
||||
# 旋转速率 (8位有符号整数)
|
||||
rot_value = int(bit_string[pos:pos+8], 2)
|
||||
if rot_value > 127:
|
||||
rot_value -= 256
|
||||
decoded['rot'] = rot_value
|
||||
pos += 8
|
||||
|
||||
decoded['sog'] = int(bit_string[pos:pos+10], 2) # 地面航速 (10 bits)
|
||||
pos += 10
|
||||
decoded['position_accuracy'] = int(bit_string[pos], 2) # 位置准确度 (1 bit)
|
||||
pos += 1
|
||||
|
||||
# 经度 (28位)
|
||||
longitude_value = int(bit_string[pos:pos+28], 2)
|
||||
decoded['longitude'] = longitude_value / (60 * 10000) # 转换为度
|
||||
pos += 28
|
||||
|
||||
# 纬度 (27位)
|
||||
latitude_value = int(bit_string[pos:pos+27], 2)
|
||||
decoded['latitude'] = latitude_value / (60 * 10000) # 转换为度
|
||||
pos += 27
|
||||
|
||||
decoded['cog'] = int(bit_string[pos:pos+12], 2) # 地面航线 (12 bits)
|
||||
pos += 12
|
||||
decoded['true_heading'] = int(bit_string[pos:pos+9], 2) # 实际航向 (9 bits)
|
||||
pos += 9
|
||||
decoded['timestamp'] = int(bit_string[pos:pos+6], 2) # 时戳 (6 bits)
|
||||
pos += 6
|
||||
decoded['special_maneuver'] = int(bit_string[pos:pos+2], 2) # 特定操纵指示符 (2 bits)
|
||||
pos += 2
|
||||
decoded['spare'] = int(bit_string[pos:pos+3], 2) # 备用 (3 bits)
|
||||
pos += 3
|
||||
decoded['raim_flag'] = int(bit_string[pos], 2) # RAIM标志 (1 bit)
|
||||
pos += 1
|
||||
decoded['comm_state'] = int(bit_string[pos:pos+19], 2) # 通信状态 (19 bits)
|
||||
|
||||
return decoded
|
||||
|
||||
def receive_data():
|
||||
"""接收数据"""
|
||||
host = 'localhost'
|
||||
port = 8082
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
s.bind((host, port))
|
||||
print(f"监听 {host}:{port}...")
|
||||
|
||||
while True:
|
||||
try:
|
||||
data, addr = s.recvfrom(4096) # 接收缓冲区大小
|
||||
print(f"\n收到来自 {addr} 的数据:")
|
||||
print("原始数据(十六进制):", data.hex())
|
||||
get_tcp_data(data)
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n停止接收")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"接收错误: {e}")
|
||||
break
|
||||
|
||||
if __name__ == "__main__":
|
||||
receive_data()
|
||||
232
报文模拟解析/develop/send - 副本.py
Normal file
232
报文模拟解析/develop/send - 副本.py
Normal file
@@ -0,0 +1,232 @@
|
||||
import struct
|
||||
import socket
|
||||
import time
|
||||
import random
|
||||
from random import uniform, randint
|
||||
|
||||
SHIP_MMSIS = [random.randint(336295939, 336295969) for _ in range(10)]
|
||||
YANGTZE_ESTUARY_LAT = 31.23
|
||||
YANGTZE_ESTUARY_LON = 122.47
|
||||
RANGE_NM = 5 # 5海里范围
|
||||
def generate_random_position():
|
||||
"""生成随机船只位置(基于长江口基准坐标)"""
|
||||
# 1海里=1.852公里≈0.0166667度(纬度)
|
||||
lat = YANGTZE_ESTUARY_LAT + (random.random() - 0.5) * RANGE_NM * 0.0166667
|
||||
lon = YANGTZE_ESTUARY_LON + (random.random() - 0.5) * RANGE_NM * 0.0166667
|
||||
|
||||
# 转换为度分格式*10000000
|
||||
longitude = int(lon * 10000000) # 东经为正数
|
||||
latitude = int(lat * 10000000) # 北纬为正数
|
||||
|
||||
return longitude, latitude
|
||||
def create_tcp_packet(packet_type, content):
|
||||
"""创建符合VDES TCP协议的报文"""
|
||||
# 报文帧头 (4字节)
|
||||
header = b'####'
|
||||
|
||||
# 报文类型 (4字节,大端)
|
||||
type_field = struct.pack('>I', packet_type)
|
||||
|
||||
# 报文长度 (4字节,大端,内容长度)
|
||||
length = len(content)
|
||||
length_field = struct.pack('>I', length)
|
||||
|
||||
# 构建完整报文
|
||||
packet = header + type_field + length_field + content
|
||||
return packet
|
||||
|
||||
def create_gps_packet():
|
||||
"""创建GPS解析数据报文(类型7)的内容"""
|
||||
# 模拟GPS数据 (表11定义)
|
||||
lon, lat = generate_random_position()
|
||||
utc_time = 1680000000 # UTC时间 (8字节)
|
||||
longitude = lon # 经度 (4字节,东经120.5度)
|
||||
latitude = lat # 纬度 (4字节,北纬30.5度)
|
||||
altitude = 50 # 海拔高度 (4字节,50米)
|
||||
used_sats = 10 # 定位使用的卫星数 (2字节)
|
||||
gps_sats = 8 # GPS卫星数 (2字节)
|
||||
beidou_sats = 7 # 北斗卫星数 (2字节)
|
||||
gps_status = 1 # GPS状态 (1字节,单点定位)
|
||||
position_mode = 3 # 定位模式 (1字节,3D定位)
|
||||
hdop = 2550 # 水平精度因子 (4字节,25.5米)
|
||||
speed = 1024 # 运动速度 (4字节,10.24节)
|
||||
direction = randint(0, 359) # 运动方向 (4字节,359度)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>QiiiHHHBBIII', # 格式: Q(8),i(4),i(4),i(4),H(2),H(2),H(2),B(1),B(1),I(4),I(4),I(4)
|
||||
utc_time,
|
||||
longitude,
|
||||
latitude,
|
||||
altitude,
|
||||
used_sats,
|
||||
gps_sats,
|
||||
beidou_sats,
|
||||
gps_status,
|
||||
position_mode,
|
||||
hdop,
|
||||
speed,
|
||||
direction
|
||||
)
|
||||
return content
|
||||
|
||||
def create_ais_info_packet(demod_content):
|
||||
"""创建解调信息报文(类型12)的内容"""
|
||||
# 模拟解调信息数据
|
||||
message_type = 12 # 消息类型 (1字节)
|
||||
satellite_id = 1 # 卫星ID (1字节)
|
||||
demodulator_id = 0 # 解调器编号 (1字节)
|
||||
demod_slot = 1234 # 解调时隙 (2字节)
|
||||
receive_delay = 5678 # 接收延迟 (2字节,单位us)
|
||||
snr = 25.75 # 信噪比 (2字节,dB)
|
||||
|
||||
# 处理信噪比 (高8位整数部分,低8位小数部分)
|
||||
snr_int = int(snr)
|
||||
snr_frac = int((snr - snr_int) * 100) # 转换为0-99的小数部分
|
||||
snr_packed = (snr_int & 0xFF) << 8 | (snr_frac & 0xFF)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>BBBHHH', # 格式: B(1),B(1),B(1),H(2),H(2),h(2)
|
||||
message_type,
|
||||
satellite_id,
|
||||
demodulator_id,
|
||||
demod_slot,
|
||||
receive_delay,
|
||||
snr_packed
|
||||
)
|
||||
return content + demod_content
|
||||
|
||||
def encode_demod_fields():
|
||||
"""编码解调信息字段并打印解码结果"""
|
||||
# 初始化二进制位列表
|
||||
bits = []
|
||||
|
||||
# 示例数据
|
||||
message_id = 1 # 消息ID (6 bits)
|
||||
repeat_indicator = 0 # 转发指示符 (2 bits)
|
||||
user_id = random.choice(SHIP_MMSIS) # 用户ID/MMSI (30 bits)
|
||||
navigation_status = 8 # 导航状态 (4 bits) - 航行中
|
||||
rot = -128 # 旋转速率 (8 bits) - 无信息
|
||||
sog = 102 # 地面航速 (10 bits) - 10.2节
|
||||
position_accuracy = 1 # 位置准确度 (1 bit) - 高
|
||||
longitude = random.uniform(122.0, 124.0) + (random.random() - 0.5) * 10 * 0.0166667 # 经度 (28 bits) - 东经120.5°
|
||||
latitude = random.uniform(30.5, 32.0) + (random.random() - 0.5) * 10 * 0.0166667 # 纬度 (27 bits) - 北纬30.5°
|
||||
cog = randint(0, 359) # 地面航线 (12 bits) - 90.0°
|
||||
true_heading = randint(0, 359) # 实际航向 (9 bits) - 90°
|
||||
timestamp = 30 # 时戳 (6 bits) - UTC秒
|
||||
special_maneuver = 0 # 特定操纵指示符 (2 bits)
|
||||
spare = 0 # 备用 (3 bits)
|
||||
raim_flag = 0 # RAIM标志 (1 bit)
|
||||
comm_state = 0 # 通信状态 (19 bits)
|
||||
|
||||
# 添加各个字段(按照协议顺序)
|
||||
bits.append(format(message_id, '06b')) # 消息ID
|
||||
bits.append(format(repeat_indicator, '02b')) # 转发指示符
|
||||
bits.append(format(user_id, '030b')) # 用户ID
|
||||
bits.append(format(navigation_status, '04b')) # 导航状态
|
||||
|
||||
# 旋转速率 (8位有符号整数)
|
||||
rot_bits = rot & 0xFF
|
||||
bits.append(format(rot_bits, '08b'))
|
||||
|
||||
# 地面航速 (10位)
|
||||
bits.append(format(min(sog, 1023), '010b'))
|
||||
|
||||
# 位置准确度 (1位)
|
||||
bits.append(str(position_accuracy))
|
||||
|
||||
# 经度 (28位)
|
||||
# 转换为1/10,000分钟单位
|
||||
longitude_min = int(longitude * 60 * 10000)
|
||||
bits.append(format(longitude_min & 0x0FFFFFFF, '028b'))
|
||||
|
||||
# 纬度 (27位)
|
||||
latitude_min = int(latitude * 60 * 10000)
|
||||
bits.append(format(latitude_min & 0x07FFFFFF, '027b'))
|
||||
|
||||
# 地面航线 (12位)
|
||||
bits.append(format(min(cog, 3600), '012b'))
|
||||
|
||||
# 实际航向 (9位)
|
||||
bits.append(format(min(true_heading, 511), '09b'))
|
||||
|
||||
# 时戳 (6位)
|
||||
bits.append(format(min(timestamp, 63), '06b'))
|
||||
|
||||
# 特定操纵指示符 (2位)
|
||||
bits.append(format(special_maneuver, '02b'))
|
||||
|
||||
# 备用 (3位)
|
||||
bits.append(format(spare, '03b'))
|
||||
|
||||
# RAIM标志 (1位)
|
||||
bits.append(str(raim_flag))
|
||||
|
||||
# 通信状态 (19位)
|
||||
bits.append(format(comm_state, '019b'))
|
||||
|
||||
# 合并所有位
|
||||
bit_string = ''.join(bits)
|
||||
|
||||
# 验证总位数
|
||||
total_bits = len(bit_string)
|
||||
if total_bits != 168:
|
||||
print(f"警告: 总位数应为168,实际为{total_bits}")
|
||||
|
||||
# 将位字符串转换为字节
|
||||
binary_int = int(bit_string, 2) # 将二进制字符串转换为整数
|
||||
byte_length = (len(bit_string) + 7) // 8 # 计算需要的字节数
|
||||
binary_data = binary_int.to_bytes(byte_length, 'big') # 转换为字节
|
||||
return binary_data
|
||||
|
||||
|
||||
def print_binary(data):
|
||||
"""打印二进制数据"""
|
||||
print("十六进制表示:", data.hex())
|
||||
print("二进制表示:", ' '.join(format(byte, '08b') for byte in data))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def send_data_continuously():
|
||||
"""持续发送AIS和GPS数据到指定端口"""
|
||||
host = 'localhost'
|
||||
port = 8082
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.connect((host, port)) # 必须先建立连接
|
||||
print(f"模拟发送数据到 {host}:{port} (TCP)")
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 生成并发送AIS报文
|
||||
demod_content = encode_demod_fields()
|
||||
ais_data = create_ais_info_packet(demod_content)
|
||||
ais_packet = create_tcp_packet(12, ais_data)
|
||||
s.sendall(ais_packet)
|
||||
print("AIS报文:")
|
||||
print_binary(ais_packet)
|
||||
print("已发送AIS报文")
|
||||
|
||||
# 生成并发送GPS报文
|
||||
gps_content = create_gps_packet()
|
||||
gps_packet = create_tcp_packet(7, gps_content)
|
||||
s.sendall(gps_packet)
|
||||
print("\nGPS报文:")
|
||||
print_binary(gps_packet)
|
||||
print("已发送GPS报文")
|
||||
|
||||
# 等待1秒
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n停止发送数据")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"发送数据时出错: {e}")
|
||||
break
|
||||
|
||||
if __name__ == "__main__":
|
||||
send_data_continuously()
|
||||
316
报文模拟解析/develop/send.py
Normal file
316
报文模拟解析/develop/send.py
Normal file
@@ -0,0 +1,316 @@
|
||||
import struct
|
||||
import socket
|
||||
import time
|
||||
import random
|
||||
from random import uniform, randint
|
||||
import math
|
||||
|
||||
SHIP_MMSIS = [random.randint(336295939, 336295969) for _ in range(10)]
|
||||
SHIP_STATUS = {}
|
||||
# 生成包含30艘船信息的列表(MMSI、纬度、经度、航向)
|
||||
SHIP_LIST = [
|
||||
(
|
||||
336295939 + i, # MMSI号
|
||||
round(random.uniform(30.0, 34.0), 6),
|
||||
round(random.uniform(123.5, 126.0), 6),
|
||||
random.randint(0, 359)
|
||||
) for i in range(30)
|
||||
]
|
||||
|
||||
# 初始化船只状态
|
||||
for ship in SHIP_LIST:
|
||||
mmsi, init_lat, init_lon, heading = ship
|
||||
SHIP_STATUS[mmsi] = {
|
||||
'current_lat': init_lat,
|
||||
'current_lon': init_lon,
|
||||
'heading': heading,
|
||||
'speed': 280.24 #加速100倍用于演示
|
||||
}
|
||||
|
||||
YANGTZE_ESTUARY_LAT = 31.23
|
||||
YANGTZE_ESTUARY_LON = 123.87
|
||||
RANGE_NM = 5 # 5海里范围
|
||||
current_lat = YANGTZE_ESTUARY_LAT # 当前纬度
|
||||
current_lon = YANGTZE_ESTUARY_LON # 当前经度
|
||||
own_heading = 90 # 固定航向 (50度)
|
||||
|
||||
def generate_random_position():
|
||||
"""生成船只位置(基于当前位置和航向计算)"""
|
||||
global current_lat, current_lon, own_heading
|
||||
|
||||
# 速度为10.24节(来自GPS报文设置)
|
||||
speed_knots = 300.24 #加速100倍用于演示
|
||||
# 转换为公里/小时 (1节 = 1.852公里/小时)
|
||||
speed_kmh = speed_knots * 1.852
|
||||
# 转换为公里/秒 (发送间隔为1秒)
|
||||
speed_kmps = speed_kmh / 3600
|
||||
# 计算1秒内移动的距离(公里)
|
||||
distance_km = speed_kmps * 1
|
||||
|
||||
# 将航向转换为弧度
|
||||
heading_rad = math.radians(own_heading)
|
||||
|
||||
# 计算纬度和经度变化
|
||||
# 1度纬度 ≈ 111公里
|
||||
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
|
||||
# 1度经度 ≈ 111公里 * cos(纬度)
|
||||
delta_lon = distance_km / (111.0 * math.cos(math.radians(current_lat))) * math.sin(heading_rad)
|
||||
|
||||
# 更新当前位置
|
||||
current_lat += delta_lat
|
||||
current_lon += delta_lon
|
||||
|
||||
# 转换为度分格式*10^7
|
||||
longitude = int(current_lon * 10000000)
|
||||
latitude = int(current_lat * 10000000)
|
||||
|
||||
return longitude, latitude
|
||||
|
||||
def generate_ship_position(mmsi):
|
||||
"""根据船只MMSI生成当前位置"""
|
||||
global SHIP_STATUS
|
||||
ship = SHIP_STATUS[mmsi] #获取全局船只中的一个进行修改
|
||||
speed_knots = ship['speed']
|
||||
speed_kmh = speed_knots * 1.852
|
||||
speed_kmps = speed_kmh / 3600
|
||||
distance_km = speed_kmps * 1
|
||||
|
||||
# 航向转弧度
|
||||
heading_rad = math.radians(ship['heading'])
|
||||
|
||||
# 计算经纬度变化
|
||||
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
|
||||
delta_lon = distance_km / (111.0 * math.cos(math.radians(ship['current_lat']))) * math.sin(heading_rad)
|
||||
|
||||
# 更新船只状态
|
||||
ship['current_lat'] += delta_lat
|
||||
ship['current_lon'] += delta_lon
|
||||
|
||||
# 返回新位置
|
||||
return ship['current_lon'], ship['current_lat']
|
||||
|
||||
def create_tcp_packet(packet_type, content):
|
||||
"""创建符合VDES TCP协议的报文"""
|
||||
# 报文帧头 (4字节)
|
||||
header = b'####'
|
||||
|
||||
# 报文类型 (4字节,大端)
|
||||
type_field = struct.pack('>I', packet_type)
|
||||
|
||||
# 报文长度 (4字节,大端,内容长度)
|
||||
length = len(content)
|
||||
length_field = struct.pack('>I', length)
|
||||
|
||||
# 构建完整报文
|
||||
packet = header + type_field + length_field + content
|
||||
return packet
|
||||
|
||||
def create_gps_packet():
|
||||
"""创建GPS解析数据报文(类型7)的内容"""
|
||||
# 模拟GPS数据 (表11定义)
|
||||
lon, lat = generate_random_position()
|
||||
utc_time = 1680000000 # UTC时间 (8字节)
|
||||
longitude = lon # 经度 (4字节,东经120.5度)
|
||||
latitude = lat # 纬度 (4字节,北纬30.5度)
|
||||
altitude = 50 # 海拔高度 (4字节,50米)
|
||||
used_sats = 10 # 定位使用的卫星数 (2字节)
|
||||
gps_sats = 8 # GPS卫星数 (2字节)
|
||||
beidou_sats = 7 # 北斗卫星数 (2字节)
|
||||
gps_status = 1 # GPS状态 (1字节,单点定位)
|
||||
position_mode = 3 # 定位模式 (1字节,3D定位)
|
||||
hdop = 2550 # 水平精度因子 (4字节,25.5米)
|
||||
speed = 1024 # 运动速度 (4字节,10.24节)
|
||||
direction = own_heading # 运动方向 (4字节,359度)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>QiiiHHHBBIII', # 格式: Q(8),i(4),i(4),i(4),H(2),H(2),H(2),B(1),B(1),I(4),I(4),I(4)
|
||||
utc_time,
|
||||
longitude,
|
||||
latitude,
|
||||
altitude,
|
||||
used_sats,
|
||||
gps_sats,
|
||||
beidou_sats,
|
||||
gps_status,
|
||||
position_mode,
|
||||
hdop,
|
||||
speed,
|
||||
direction
|
||||
)
|
||||
return content
|
||||
|
||||
def create_ais_info_packet(demod_content):
|
||||
"""创建解调信息报文(类型12)的内容"""
|
||||
# 模拟解调信息数据
|
||||
message_type = 12 # 消息类型 (1字节)
|
||||
satellite_id = 1 # 卫星ID (1字节)
|
||||
demodulator_id = 0 # 解调器编号 (1字节)
|
||||
demod_slot = 1234 # 解调时隙 (2字节)
|
||||
receive_delay = 5678 # 接收延迟 (2字节,单位us)
|
||||
snr = 25.75 # 信噪比 (2字节,dB)
|
||||
|
||||
# 处理信噪比 (高8位整数部分,低8位小数部分)
|
||||
snr_int = int(snr)
|
||||
snr_frac = int((snr - snr_int) * 100) # 转换为0-99的小数部分
|
||||
snr_packed = (snr_int & 0xFF) << 8 | (snr_frac & 0xFF)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>BBBHHH', # 格式: B(1),B(1),B(1),H(2),H(2),h(2)
|
||||
message_type,
|
||||
satellite_id,
|
||||
demodulator_id,
|
||||
demod_slot,
|
||||
receive_delay,
|
||||
snr_packed
|
||||
)
|
||||
return content + demod_content
|
||||
|
||||
def encode_demod_fields(SHIP_MMSI, lat, lon, heading):
|
||||
"""编码解调信息字段并打印解码结果"""
|
||||
# 初始化二进制位列表
|
||||
bits = []
|
||||
print(SHIP_MMSI, lat, lon, heading)
|
||||
# 示例数据
|
||||
message_id = 1 # 消息ID (6 bits)
|
||||
repeat_indicator = 0 # 转发指示符 (2 bits)
|
||||
user_id = SHIP_MMSI # 用户ID/MMSI (30 bits)
|
||||
navigation_status = 8 # 导航状态 (4 bits) - 航行中
|
||||
rot = -128 # 旋转速率 (8 bits) - 无信息
|
||||
sog = 102 # 地面航速 (10 bits) - 10.2节
|
||||
position_accuracy = 1 # 位置准确度 (1 bit) - 高
|
||||
longitude, latitude = lon, lat # 经纬度
|
||||
cog = heading # 地面航线 (12 bits) - 90.0°
|
||||
true_heading = heading # 实际航向 (9 bits) - 90°
|
||||
timestamp = 30 # 时戳 (6 bits) - UTC秒
|
||||
special_maneuver = 0 # 特定操纵指示符 (2 bits)
|
||||
spare = 0 # 备用 (3 bits)
|
||||
raim_flag = 0 # RAIM标志 (1 bit)
|
||||
comm_state = 0 # 通信状态 (19 bits)
|
||||
|
||||
# 添加各个字段(按照协议顺序)
|
||||
bits.append(format(message_id, '06b')) # 消息ID
|
||||
bits.append(format(repeat_indicator, '02b')) # 转发指示符
|
||||
bits.append(format(user_id, '030b')) # 用户ID
|
||||
bits.append(format(navigation_status, '04b')) # 导航状态
|
||||
|
||||
# 旋转速率 (8位有符号整数)
|
||||
rot_bits = rot & 0xFF
|
||||
bits.append(format(rot_bits, '08b'))
|
||||
|
||||
# 地面航速 (10位)
|
||||
bits.append(format(min(sog, 1023), '010b'))
|
||||
|
||||
# 位置准确度 (1位)
|
||||
bits.append(str(position_accuracy))
|
||||
|
||||
# 经度 (28位)
|
||||
# 转换为1/10,000分钟单位
|
||||
longitude_min = int(longitude * 60 * 10000)
|
||||
bits.append(format(longitude_min & 0x0FFFFFFF, '028b'))
|
||||
|
||||
# 纬度 (27位)
|
||||
latitude_min = int(latitude * 60 * 10000)
|
||||
bits.append(format(latitude_min & 0x07FFFFFF, '027b'))
|
||||
|
||||
# 地面航线 (12位)
|
||||
bits.append(format(min(cog, 3600), '012b'))
|
||||
|
||||
# 实际航向 (9位)
|
||||
bits.append(format(min(true_heading, 511), '09b'))
|
||||
|
||||
# 时戳 (6位)
|
||||
bits.append(format(min(timestamp, 63), '06b'))
|
||||
|
||||
# 特定操纵指示符 (2位)
|
||||
bits.append(format(special_maneuver, '02b'))
|
||||
|
||||
# 备用 (3位)
|
||||
bits.append(format(spare, '03b'))
|
||||
|
||||
# RAIM标志 (1位)
|
||||
bits.append(str(raim_flag))
|
||||
|
||||
# 通信状态 (19位)
|
||||
bits.append(format(comm_state, '019b'))
|
||||
|
||||
# 合并所有位
|
||||
bit_string = ''.join(bits)
|
||||
|
||||
# 验证总位数
|
||||
total_bits = len(bit_string)
|
||||
if total_bits != 168:
|
||||
print(f"警告: 总位数应为168,实际为{total_bits}")
|
||||
|
||||
# 将位字符串转换为字节
|
||||
binary_int = int(bit_string, 2) # 将二进制字符串转换为整数
|
||||
byte_length = (len(bit_string) + 7) // 8 # 计算需要的字节数
|
||||
binary_data = binary_int.to_bytes(byte_length, 'big') # 转换为字节
|
||||
return binary_data
|
||||
|
||||
|
||||
def print_binary(data):
|
||||
"""打印二进制数据"""
|
||||
print("十六进制表示:", data.hex())
|
||||
print("二进制表示:", ' '.join(format(byte, '08b') for byte in data))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def send_data_continuously():
|
||||
"""启动Socket服务端"""
|
||||
host = '127.0.0.1' # 监听所有网络接口
|
||||
port = 10
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind((host, port))
|
||||
s.listen(1)
|
||||
print(f"Socket服务端已启动,监听 {host}:{port}")
|
||||
|
||||
while True:
|
||||
|
||||
try:
|
||||
conn, addr = s.accept()
|
||||
print(f"客户端连接来自: {addr}")
|
||||
|
||||
while True:
|
||||
# 接收客户端数据
|
||||
data = conn.recv(4096) # 接收客户端数据
|
||||
if data:
|
||||
print(f"收到VDES客户端消息: {data.hex()}")
|
||||
# 向客户端发送数据
|
||||
try:
|
||||
# 生成并发送AIS报文
|
||||
selected_ship = random.choice(SHIP_LIST)
|
||||
mmsi = selected_ship[0]
|
||||
lon, lat = generate_ship_position(mmsi)
|
||||
heading = SHIP_STATUS[mmsi]['heading']
|
||||
demod_content = encode_demod_fields(mmsi, lat, lon, heading)
|
||||
ais_data = create_ais_info_packet(demod_content)
|
||||
ais_packet = create_tcp_packet(12, ais_data)
|
||||
conn.sendall(ais_packet)
|
||||
|
||||
# 生成并发送GPS报文
|
||||
gps_content = create_gps_packet()
|
||||
gps_packet = 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}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
send_data_continuously()
|
||||
230
报文模拟解析/develop/send_udp.py
Normal file
230
报文模拟解析/develop/send_udp.py
Normal file
@@ -0,0 +1,230 @@
|
||||
import struct
|
||||
import socket
|
||||
import time
|
||||
import random
|
||||
|
||||
SHIP_MMSIS = [random.randint(200000000, 299999999) for _ in range(10)]
|
||||
YANGTZE_ESTUARY_LAT = 31.23
|
||||
YANGTZE_ESTUARY_LON = 122.47
|
||||
RANGE_NM = 5 # 5海里范围
|
||||
def generate_random_position():
|
||||
"""生成随机船只位置(基于长江口基准坐标)"""
|
||||
# 1海里=1.852公里≈0.0166667度(纬度)
|
||||
lat = YANGTZE_ESTUARY_LAT + (random.random() - 0.5) * RANGE_NM * 0.0166667
|
||||
lon = YANGTZE_ESTUARY_LON + (random.random() - 0.5) * RANGE_NM * 0.0166667
|
||||
|
||||
# 转换为度分格式*10000000
|
||||
longitude = int(lon * 10000000) # 东经为正数
|
||||
latitude = int(lat * 10000000) # 北纬为正数
|
||||
|
||||
return longitude, latitude
|
||||
def create_tcp_packet(packet_type, content):
|
||||
"""创建符合VDES TCP协议的报文"""
|
||||
# 报文帧头 (4字节)
|
||||
header = b'####'
|
||||
|
||||
# 报文类型 (4字节,大端)
|
||||
type_field = struct.pack('>I', packet_type)
|
||||
|
||||
# 报文长度 (4字节,大端,内容长度)
|
||||
length = len(content)
|
||||
length_field = struct.pack('>I', length)
|
||||
|
||||
# 构建完整报文
|
||||
packet = header + type_field + length_field + content
|
||||
return packet
|
||||
|
||||
def create_gps_packet():
|
||||
"""创建GPS解析数据报文(类型7)的内容"""
|
||||
# 模拟GPS数据 (表11定义)
|
||||
lon, lat = generate_random_position()
|
||||
utc_time = 1680000000 # UTC时间 (8字节)
|
||||
longitude = lon # 经度 (4字节,东经120.5度)
|
||||
latitude = lat # 纬度 (4字节,北纬30.5度)
|
||||
altitude = 50 # 海拔高度 (4字节,50米)
|
||||
used_sats = 10 # 定位使用的卫星数 (2字节)
|
||||
gps_sats = 8 # GPS卫星数 (2字节)
|
||||
beidou_sats = 7 # 北斗卫星数 (2字节)
|
||||
gps_status = 1 # GPS状态 (1字节,单点定位)
|
||||
position_mode = 3 # 定位模式 (1字节,3D定位)
|
||||
hdop = 2550 # 水平精度因子 (4字节,25.5米)
|
||||
speed = 1024 # 运动速度 (4字节,10.24节)
|
||||
direction = 359 # 运动方向 (4字节,359度)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>QiiiHHHBBIII', # 格式: Q(8),i(4),i(4),i(4),H(2),H(2),H(2),B(1),B(1),I(4),I(4),I(4)
|
||||
utc_time,
|
||||
longitude,
|
||||
latitude,
|
||||
altitude,
|
||||
used_sats,
|
||||
gps_sats,
|
||||
beidou_sats,
|
||||
gps_status,
|
||||
position_mode,
|
||||
hdop,
|
||||
speed,
|
||||
direction
|
||||
)
|
||||
return content
|
||||
|
||||
def create_ais_info_packet(demod_content):
|
||||
"""创建解调信息报文(类型12)的内容"""
|
||||
# 模拟解调信息数据
|
||||
message_type = 12 # 消息类型 (1字节)
|
||||
satellite_id = 1 # 卫星ID (1字节)
|
||||
demodulator_id = 0 # 解调器编号 (1字节)
|
||||
demod_slot = 1234 # 解调时隙 (2字节)
|
||||
receive_delay = 5678 # 接收延迟 (2字节,单位us)
|
||||
snr = 25.75 # 信噪比 (2字节,dB)
|
||||
|
||||
# 处理信噪比 (高8位整数部分,低8位小数部分)
|
||||
snr_int = int(snr)
|
||||
snr_frac = int((snr - snr_int) * 100) # 转换为0-99的小数部分
|
||||
snr_packed = (snr_int & 0xFF) << 8 | (snr_frac & 0xFF)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>BBBHHH', # 格式: B(1),B(1),B(1),H(2),H(2),h(2)
|
||||
message_type,
|
||||
satellite_id,
|
||||
demodulator_id,
|
||||
demod_slot,
|
||||
receive_delay,
|
||||
snr_packed
|
||||
)
|
||||
return content + demod_content
|
||||
|
||||
def encode_demod_fields():
|
||||
"""编码解调信息字段并打印解码结果"""
|
||||
# 初始化二进制位列表
|
||||
bits = []
|
||||
|
||||
# 示例数据
|
||||
message_id = 1 # 消息ID (6 bits)
|
||||
repeat_indicator = 0 # 转发指示符 (2 bits)
|
||||
user_id = random.choice(SHIP_MMSIS) # 用户ID/MMSI (30 bits)
|
||||
navigation_status = 8 # 导航状态 (4 bits) - 航行中
|
||||
rot = -128 # 旋转速率 (8 bits) - 无信息
|
||||
sog = 102 # 地面航速 (10 bits) - 10.2节
|
||||
position_accuracy = 1 # 位置准确度 (1 bit) - 高
|
||||
longitude = 124.5 + (random.random() - 0.5) * RANGE_NM * 0.0166667 # 经度 (28 bits) - 东经120.5°
|
||||
latitude = 30.5 + (random.random() - 0.5) * RANGE_NM * 0.0166667 # 纬度 (27 bits) - 北纬30.5°
|
||||
cog = 900 # 地面航线 (12 bits) - 90.0°
|
||||
true_heading = 90 # 实际航向 (9 bits) - 90°
|
||||
timestamp = 30 # 时戳 (6 bits) - UTC秒
|
||||
special_maneuver = 0 # 特定操纵指示符 (2 bits)
|
||||
spare = 0 # 备用 (3 bits)
|
||||
raim_flag = 0 # RAIM标志 (1 bit)
|
||||
comm_state = 0 # 通信状态 (19 bits)
|
||||
|
||||
# 添加各个字段(按照协议顺序)
|
||||
bits.append(format(message_id, '06b')) # 消息ID
|
||||
bits.append(format(repeat_indicator, '02b')) # 转发指示符
|
||||
bits.append(format(user_id, '030b')) # 用户ID
|
||||
bits.append(format(navigation_status, '04b')) # 导航状态
|
||||
|
||||
# 旋转速率 (8位有符号整数)
|
||||
rot_bits = rot & 0xFF
|
||||
bits.append(format(rot_bits, '08b'))
|
||||
|
||||
# 地面航速 (10位)
|
||||
bits.append(format(min(sog, 1023), '010b'))
|
||||
|
||||
# 位置准确度 (1位)
|
||||
bits.append(str(position_accuracy))
|
||||
|
||||
# 经度 (28位)
|
||||
# 转换为1/10,000分钟单位
|
||||
longitude_min = int(longitude * 60 * 10000)
|
||||
bits.append(format(longitude_min & 0x0FFFFFFF, '028b'))
|
||||
|
||||
# 纬度 (27位)
|
||||
latitude_min = int(latitude * 60 * 10000)
|
||||
bits.append(format(latitude_min & 0x07FFFFFF, '027b'))
|
||||
|
||||
# 地面航线 (12位)
|
||||
bits.append(format(min(cog, 3600), '012b'))
|
||||
|
||||
# 实际航向 (9位)
|
||||
bits.append(format(min(true_heading, 511), '09b'))
|
||||
|
||||
# 时戳 (6位)
|
||||
bits.append(format(min(timestamp, 63), '06b'))
|
||||
|
||||
# 特定操纵指示符 (2位)
|
||||
bits.append(format(special_maneuver, '02b'))
|
||||
|
||||
# 备用 (3位)
|
||||
bits.append(format(spare, '03b'))
|
||||
|
||||
# RAIM标志 (1位)
|
||||
bits.append(str(raim_flag))
|
||||
|
||||
# 通信状态 (19位)
|
||||
bits.append(format(comm_state, '019b'))
|
||||
|
||||
# 合并所有位
|
||||
bit_string = ''.join(bits)
|
||||
|
||||
# 验证总位数
|
||||
total_bits = len(bit_string)
|
||||
if total_bits != 168:
|
||||
print(f"警告: 总位数应为168,实际为{total_bits}")
|
||||
|
||||
# 将位字符串转换为字节
|
||||
binary_int = int(bit_string, 2) # 将二进制字符串转换为整数
|
||||
byte_length = (len(bit_string) + 7) // 8 # 计算需要的字节数
|
||||
binary_data = binary_int.to_bytes(byte_length, 'big') # 转换为字节
|
||||
return binary_data
|
||||
|
||||
|
||||
def print_binary(data):
|
||||
"""打印二进制数据"""
|
||||
print("十六进制表示:", data.hex())
|
||||
print("二进制表示:", ' '.join(format(byte, '08b') for byte in data))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def send_data_continuously():
|
||||
"""持续发送AIS和GPS数据到指定端口"""
|
||||
host = 'localhost'
|
||||
port = 8082
|
||||
# 无连接型
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
print(f"模拟发送数据到 {host}:{port} (UDP)")
|
||||
while True:
|
||||
try:
|
||||
# 生成并发送AIS报文
|
||||
demod_content = encode_demod_fields()
|
||||
ais_data = create_ais_info_packet(demod_content)
|
||||
ais_packet = create_tcp_packet(12, ais_data)
|
||||
s.sendto(ais_packet, (host, port))
|
||||
print("AIS报文:")
|
||||
print_binary(ais_packet)
|
||||
print("已发送AIS报文")
|
||||
|
||||
# 生成并发送GPS报文
|
||||
gps_content = create_gps_packet()
|
||||
gps_packet = create_tcp_packet(7, gps_content)
|
||||
s.sendto(gps_packet, (host, port))
|
||||
print("\nGPS报文:")
|
||||
print_binary(gps_packet)
|
||||
print("已发送GPS报文")
|
||||
|
||||
# 等待1秒
|
||||
time.sleep(3)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n停止发送数据")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"发送数据时出错: {e}")
|
||||
break
|
||||
|
||||
if __name__ == "__main__":
|
||||
send_data_continuously()
|
||||
307
报文模拟解析/develop/send模拟航迹线.py
Normal file
307
报文模拟解析/develop/send模拟航迹线.py
Normal file
@@ -0,0 +1,307 @@
|
||||
import struct
|
||||
import socket
|
||||
import time
|
||||
import random
|
||||
from random import uniform, randint
|
||||
import math
|
||||
|
||||
SHIP_MMSIS = [random.randint(336295939, 336295969) for _ in range(10)]
|
||||
SHIP_STATUS = {}
|
||||
# 生成包含30艘船信息的列表(MMSI、纬度、经度、航向)
|
||||
SHIP_LIST = [
|
||||
(
|
||||
336295939 + i, # MMSI号
|
||||
round(random.uniform(30.0, 34.0), 6),
|
||||
round(random.uniform(123.5, 126.0), 6),
|
||||
random.randint(0, 359)
|
||||
) for i in range(30)
|
||||
]
|
||||
|
||||
# 初始化船只状态
|
||||
for ship in SHIP_LIST:
|
||||
mmsi, init_lat, init_lon, heading = ship
|
||||
SHIP_STATUS[mmsi] = {
|
||||
'current_lat': init_lat,
|
||||
'current_lon': init_lon,
|
||||
'heading': heading,
|
||||
'speed': 280.24 #加速100倍用于演示
|
||||
}
|
||||
|
||||
YANGTZE_ESTUARY_LAT = 31.23
|
||||
YANGTZE_ESTUARY_LON = 123.87
|
||||
RANGE_NM = 5 # 5海里范围
|
||||
current_lat = YANGTZE_ESTUARY_LAT # 当前纬度
|
||||
current_lon = YANGTZE_ESTUARY_LON # 当前经度
|
||||
own_heading = 90 # 固定航向 (50度)
|
||||
|
||||
def generate_random_position():
|
||||
"""生成船只位置(基于当前位置和航向计算)"""
|
||||
global current_lat, current_lon, own_heading
|
||||
|
||||
# 速度为10.24节(来自GPS报文设置)
|
||||
speed_knots = 300.24 #加速100倍用于演示
|
||||
# 转换为公里/小时 (1节 = 1.852公里/小时)
|
||||
speed_kmh = speed_knots * 1.852
|
||||
# 转换为公里/秒 (发送间隔为1秒)
|
||||
speed_kmps = speed_kmh / 3600
|
||||
# 计算1秒内移动的距离(公里)
|
||||
distance_km = speed_kmps * 1
|
||||
|
||||
# 将航向转换为弧度
|
||||
heading_rad = math.radians(own_heading)
|
||||
|
||||
# 计算纬度和经度变化
|
||||
# 1度纬度 ≈ 111公里
|
||||
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
|
||||
# 1度经度 ≈ 111公里 * cos(纬度)
|
||||
delta_lon = distance_km / (111.0 * math.cos(math.radians(current_lat))) * math.sin(heading_rad)
|
||||
|
||||
# 更新当前位置
|
||||
current_lat += delta_lat
|
||||
current_lon += delta_lon
|
||||
|
||||
# 转换为度分格式*10^7
|
||||
longitude = int(current_lon * 10000000)
|
||||
latitude = int(current_lat * 10000000)
|
||||
|
||||
return longitude, latitude
|
||||
|
||||
def generate_ship_position(mmsi):
|
||||
"""根据船只MMSI生成当前位置"""
|
||||
global SHIP_STATUS
|
||||
ship = SHIP_STATUS[mmsi] #获取全局船只中的一个进行修改
|
||||
speed_knots = ship['speed']
|
||||
speed_kmh = speed_knots * 1.852
|
||||
speed_kmps = speed_kmh / 3600
|
||||
distance_km = speed_kmps * 1
|
||||
|
||||
# 航向转弧度
|
||||
heading_rad = math.radians(ship['heading'])
|
||||
|
||||
# 计算经纬度变化
|
||||
delta_lat = distance_km / 111.0 * math.cos(heading_rad)
|
||||
delta_lon = distance_km / (111.0 * math.cos(math.radians(ship['current_lat']))) * math.sin(heading_rad)
|
||||
|
||||
# 更新船只状态
|
||||
ship['current_lat'] += delta_lat
|
||||
ship['current_lon'] += delta_lon
|
||||
|
||||
# 返回新位置
|
||||
return ship['current_lon'], ship['current_lat']
|
||||
|
||||
def create_tcp_packet(packet_type, content):
|
||||
"""创建符合VDES TCP协议的报文"""
|
||||
# 报文帧头 (4字节)
|
||||
header = b'####'
|
||||
|
||||
# 报文类型 (4字节,大端)
|
||||
type_field = struct.pack('>I', packet_type)
|
||||
|
||||
# 报文长度 (4字节,大端,内容长度)
|
||||
length = len(content)
|
||||
length_field = struct.pack('>I', length)
|
||||
|
||||
# 构建完整报文
|
||||
packet = header + type_field + length_field + content
|
||||
return packet
|
||||
|
||||
def create_gps_packet():
|
||||
"""创建GPS解析数据报文(类型7)的内容"""
|
||||
# 模拟GPS数据 (表11定义)
|
||||
lon, lat = generate_random_position()
|
||||
utc_time = 1680000000 # UTC时间 (8字节)
|
||||
longitude = lon # 经度 (4字节,东经120.5度)
|
||||
latitude = lat # 纬度 (4字节,北纬30.5度)
|
||||
altitude = 50 # 海拔高度 (4字节,50米)
|
||||
used_sats = 10 # 定位使用的卫星数 (2字节)
|
||||
gps_sats = 8 # GPS卫星数 (2字节)
|
||||
beidou_sats = 7 # 北斗卫星数 (2字节)
|
||||
gps_status = 1 # GPS状态 (1字节,单点定位)
|
||||
position_mode = 3 # 定位模式 (1字节,3D定位)
|
||||
hdop = 2550 # 水平精度因子 (4字节,25.5米)
|
||||
speed = 1024 # 运动速度 (4字节,10.24节)
|
||||
direction = own_heading # 运动方向 (4字节,359度)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>QiiiHHHBBIII', # 格式: Q(8),i(4),i(4),i(4),H(2),H(2),H(2),B(1),B(1),I(4),I(4),I(4)
|
||||
utc_time,
|
||||
longitude,
|
||||
latitude,
|
||||
altitude,
|
||||
used_sats,
|
||||
gps_sats,
|
||||
beidou_sats,
|
||||
gps_status,
|
||||
position_mode,
|
||||
hdop,
|
||||
speed,
|
||||
direction
|
||||
)
|
||||
return content
|
||||
|
||||
def create_ais_info_packet(demod_content):
|
||||
"""创建解调信息报文(类型12)的内容"""
|
||||
# 模拟解调信息数据
|
||||
message_type = 12 # 消息类型 (1字节)
|
||||
satellite_id = 1 # 卫星ID (1字节)
|
||||
demodulator_id = 0 # 解调器编号 (1字节)
|
||||
demod_slot = 1234 # 解调时隙 (2字节)
|
||||
receive_delay = 5678 # 接收延迟 (2字节,单位us)
|
||||
snr = 25.75 # 信噪比 (2字节,dB)
|
||||
|
||||
# 处理信噪比 (高8位整数部分,低8位小数部分)
|
||||
snr_int = int(snr)
|
||||
snr_frac = int((snr - snr_int) * 100) # 转换为0-99的小数部分
|
||||
snr_packed = (snr_int & 0xFF) << 8 | (snr_frac & 0xFF)
|
||||
|
||||
# 打包为二进制 (大端字节序)
|
||||
content = struct.pack(
|
||||
'>BBBHHH', # 格式: B(1),B(1),B(1),H(2),H(2),h(2)
|
||||
message_type,
|
||||
satellite_id,
|
||||
demodulator_id,
|
||||
demod_slot,
|
||||
receive_delay,
|
||||
snr_packed
|
||||
)
|
||||
return content + demod_content
|
||||
|
||||
def encode_demod_fields(SHIP_MMSI, lat, lon, heading):
|
||||
"""编码解调信息字段并打印解码结果"""
|
||||
# 初始化二进制位列表
|
||||
bits = []
|
||||
print(SHIP_MMSI, lat, lon, heading)
|
||||
# 示例数据
|
||||
message_id = 1 # 消息ID (6 bits)
|
||||
repeat_indicator = 0 # 转发指示符 (2 bits)
|
||||
user_id = SHIP_MMSI # 用户ID/MMSI (30 bits)
|
||||
navigation_status = 8 # 导航状态 (4 bits) - 航行中
|
||||
rot = -128 # 旋转速率 (8 bits) - 无信息
|
||||
sog = 102 # 地面航速 (10 bits) - 10.2节
|
||||
position_accuracy = 1 # 位置准确度 (1 bit) - 高
|
||||
longitude, latitude = lon, lat # 经纬度
|
||||
cog = heading # 地面航线 (12 bits) - 90.0°
|
||||
true_heading = heading # 实际航向 (9 bits) - 90°
|
||||
timestamp = 30 # 时戳 (6 bits) - UTC秒
|
||||
special_maneuver = 0 # 特定操纵指示符 (2 bits)
|
||||
spare = 0 # 备用 (3 bits)
|
||||
raim_flag = 0 # RAIM标志 (1 bit)
|
||||
comm_state = 0 # 通信状态 (19 bits)
|
||||
|
||||
# 添加各个字段(按照协议顺序)
|
||||
bits.append(format(message_id, '06b')) # 消息ID
|
||||
bits.append(format(repeat_indicator, '02b')) # 转发指示符
|
||||
bits.append(format(user_id, '030b')) # 用户ID
|
||||
bits.append(format(navigation_status, '04b')) # 导航状态
|
||||
|
||||
# 旋转速率 (8位有符号整数)
|
||||
rot_bits = rot & 0xFF
|
||||
bits.append(format(rot_bits, '08b'))
|
||||
|
||||
# 地面航速 (10位)
|
||||
bits.append(format(min(sog, 1023), '010b'))
|
||||
|
||||
# 位置准确度 (1位)
|
||||
bits.append(str(position_accuracy))
|
||||
|
||||
# 经度 (28位)
|
||||
# 转换为1/10,000分钟单位
|
||||
longitude_min = int(longitude * 60 * 10000)
|
||||
bits.append(format(longitude_min & 0x0FFFFFFF, '028b'))
|
||||
|
||||
# 纬度 (27位)
|
||||
latitude_min = int(latitude * 60 * 10000)
|
||||
bits.append(format(latitude_min & 0x07FFFFFF, '027b'))
|
||||
|
||||
# 地面航线 (12位)
|
||||
bits.append(format(min(cog, 3600), '012b'))
|
||||
|
||||
# 实际航向 (9位)
|
||||
bits.append(format(min(true_heading, 511), '09b'))
|
||||
|
||||
# 时戳 (6位)
|
||||
bits.append(format(min(timestamp, 63), '06b'))
|
||||
|
||||
# 特定操纵指示符 (2位)
|
||||
bits.append(format(special_maneuver, '02b'))
|
||||
|
||||
# 备用 (3位)
|
||||
bits.append(format(spare, '03b'))
|
||||
|
||||
# RAIM标志 (1位)
|
||||
bits.append(str(raim_flag))
|
||||
|
||||
# 通信状态 (19位)
|
||||
bits.append(format(comm_state, '019b'))
|
||||
|
||||
# 合并所有位
|
||||
bit_string = ''.join(bits)
|
||||
|
||||
# 验证总位数
|
||||
total_bits = len(bit_string)
|
||||
if total_bits != 168:
|
||||
print(f"警告: 总位数应为168,实际为{total_bits}")
|
||||
|
||||
# 将位字符串转换为字节
|
||||
binary_int = int(bit_string, 2) # 将二进制字符串转换为整数
|
||||
byte_length = (len(bit_string) + 7) // 8 # 计算需要的字节数
|
||||
binary_data = binary_int.to_bytes(byte_length, 'big') # 转换为字节
|
||||
return binary_data
|
||||
|
||||
|
||||
def print_binary(data):
|
||||
"""打印二进制数据"""
|
||||
print("十六进制表示:", data.hex())
|
||||
print("二进制表示:", ' '.join(format(byte, '08b') for byte in data))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def send_data_continuously():
|
||||
"""持续发送AIS和GPS数据到指定端口"""
|
||||
host = 'localhost'
|
||||
port = 8082
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.connect((host, port)) # 必须先建立连接
|
||||
print(f"模拟发送数据到 {host}:{port} (TCP)")
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 生成并发送AIS报文
|
||||
# 从SHIP_LIST随机抽取一艘船的信息
|
||||
selected_ship = random.choice(SHIP_LIST)
|
||||
mmsi = selected_ship[0]
|
||||
# 生成该船的当前位置
|
||||
lon, lat = generate_ship_position(mmsi)
|
||||
heading = SHIP_STATUS[mmsi]['heading']
|
||||
demod_content = encode_demod_fields(mmsi, lat, lon, heading)
|
||||
ais_data = create_ais_info_packet(demod_content)
|
||||
ais_packet = create_tcp_packet(12, ais_data)
|
||||
s.sendall(ais_packet)
|
||||
print("AIS报文:")
|
||||
print_binary(ais_packet)
|
||||
print("已发送AIS报文")
|
||||
|
||||
# 生成并发送GPS报文
|
||||
gps_content = create_gps_packet()
|
||||
gps_packet = create_tcp_packet(7, gps_content)
|
||||
s.sendall(gps_packet)
|
||||
print("\nGPS报文:")
|
||||
print_binary(gps_packet)
|
||||
print("已发送GPS报文")
|
||||
|
||||
# 等待1秒
|
||||
time.sleep(0.5)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n停止发送数据")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"发送数据时出错: {e}")
|
||||
break
|
||||
|
||||
if __name__ == "__main__":
|
||||
send_data_continuously()
|
||||
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