我正在编写一个涉及消息
transmission
和使用reception
的16 QAM mapping
的Python代码。但是,我遇到了一个问题,即我没有按预期收到原始消息。
代码:
import numpy as np
import string
import random
from difflib import SequenceMatcher
def transmitter(message):
binary_message = ''.join(format(ord(char), '08b') for char in message)
binary_chunks = [binary_message[i:i + 4] for i in range(0, len(binary_message), 4)]
symbol_map = {'0000': complex(1, 1),
'0001': complex(1, 3),
'0010': complex(3, 1),
'0011': complex(3, 3),
'0100': complex(-1, 1),
'0101': complex(-1, 3),
'0110': complex(-3, 1),
'0111': complex(-3, 3),
'1000': complex(1, -1),
'1001': complex(1, -3),
'1010': complex(3, -1),
'1011': complex(3, -3),
'1100': complex(-1, -1),
'1101': complex(-1, -3),
'1110': complex(-3, -1),
'1111': complex(-3, -3)}
qam_signal = [symbol_map[chunk] for chunk in binary_chunks]
signal_parts = [(sample.real, sample.imag) for sample in qam_signal]
flat_signal = [part for sample in signal_parts for part in sample]
return flat_signal
def channel(sent_signal, noise_factor=1):
sent_signal = np.array(sent_signal)
assert np.size(sent_signal) <= 400, "n must be <= 200"
n = np.size(sent_signal) // 2
x = sent_signal[0:2*n]
s = np.sum(x**2) / np.size(x)
sigma = 1
if s > 1:
sigma = np.sqrt(s)
Z = np.random.normal(0, sigma*noise_factor, size=(2*n,))
A = np.array([[11, 10], [10, 11]])
B = np.kron(np.eye(n), A)
Y = B.dot(x) + Z.T
return Y
def receiver(received_signal):
def find_closest_point(point, symbol_map):
# Find the constellation point closest to the received point
distances = [np.abs(point - constellation_point) for constellation_point in symbol_map.keys()]
closest_point = min(distances)
closest_index = distances.index(closest_point)
closest_complex = list(symbol_map.keys())[closest_index]
closest_binary = symbol_map[closest_complex]
return closest_binary
received_signal = received_signal.flatten()
qam_signal = [complex(received_signal[i], received_signal[i + 1]) for i in range(0, len(received_signal), 2)]
# 16-QAM demodulation
symbol_map = {complex(1, 1): '0000',
complex(1, 3): '0001',
complex(3, 1): '0010',
complex(3, 3): '0011',
complex(-1, 1): '0100',
complex(-1, 3): '0101',
complex(-3, 1): '0110',
complex(-3, 3): '0111',
complex(1, -1): '1000',
complex(1, -3): '1001',
complex(3, -1): '1010',
complex(3, -3): '1011',
complex(-1, -1): '1100',
complex(-1, -3): '1101',
complex(-3, -1): '1110',
complex(-3, -3): '1111'}
demodulated_signal = [find_closest_point(point, symbol_map) for point in qam_signal]
binary_message = ''.join(demodulated_signal)
text_message = bytes([int(binary_message[i:i + 8], 2) for i in range(0, len(binary_message), 8)]).decode('latin-1')
return text_message
def generate_random_string(length):
# All ASCII characters
ascii_characters = string.ascii_letters + string.digits + string.punctuation
# Generate the random string
random_string = ''.join(random.choice(ascii_characters) for _ in range(length))
return random_string
# Example usage:
message = generate_random_string(50)
X = transmitter(message) # Encode our message
Y = channel(X, noise_factor=0.5) # Simulate the treatment done by the channel
reconstructed_message = receiver(Y) # Decode the message received by the channel
print("Original message:", message)
print("Reconstructed message:", reconstructed_message)
def check_similarity(original_message, reconstructed_message):
# Create a SequenceMatcher object
matcher = SequenceMatcher(None, original_message, reconstructed_message)
# Calculate the similarity ratio
similarity_ratio = matcher.ratio()
return similarity_ratio
# Similarity check
similarity_ratio = check_similarity(message, reconstructed_message)
print(f"Similarity ratio: {similarity_ratio:.2f}")
输出:
Original message: ]?XQ52jc?>$K{~=[kC;'QveIM^c5Yzg=u6I*0A~;Tj8IXM_m)F
Reconstructed message: ??8333óó??4K{?;ûC;73óOO?ó3?s÷?s?O33C;4ó8O8O?ÿ?O
Similarity ratio: 0.16
描述
我已经实现了一个使用 16 QAM 映射来发送和接收消息的代码。该代码由以下部分组成:
transmitter
:将消息转换为二进制,填充它,并使用预定义的星座将每个符号映射到复数值。
receiver
:解调接收到的符号,检查它们是否是有效的 ASCII 字符,并重建消息。
channel
:模拟信道并向传输信号引入噪声。
generate_random_string:生成用于测试的随机消息。
问题:
我面临的问题是,当我运行代码时,重构的消息与原始消息不一样。
预期行为:
我希望重建的消息与原始消息相同。
问题:
什么可能导致我的代码中原始消息和重构消息之间的差异?
是否有任何我可能忽略的错误或改进?
如何修改代码才能保证准确重建原始消息? 任何指导、建议或解释将不胜感激。
首先感谢您提供功能齐全的代码。这很有帮助。
padded_binary_message = binary_message + '0' * (4 - len(binary_message) % 4)
您是否注意到结果数组的大小为 101?这是因为 (4 - len(binary_message) % 4) 等于 4,而不是 0。只有当模数不等于 4 时,才应该进行填充。此外,字符串的 8 位表示将始终是 4 的倍数。您应该只需删除填充即可。
def transmitter(message):
binary_message = ''.join(format(ord(c), '08b') for c in message)
symbols = [binary_message[i:i+4] for i in range(0, len(binary_message), 4)]
constellation = {
'0000': complex(-6, 6), '0001': complex(-6, 2), '0010': complex(-6, -6),
'0011': complex(-6, -2), '0100': complex(-2, 6), '0101': complex(-2, 2),
'0110': complex(-2, -6), '0111': complex(-2, -2),'1000': complex(6, 6),
'1001': complex(6, 2), '1010': complex(6, -6), '1011': complex(6, -2),
'1100': complex(2, 6), '1101': complex(2, 2), '1110': complex(2, -6),
'1111': complex(2, -2)
}
signal = [constellation[symbol] for symbol in symbols]
return signal
for constellation_point in constellation.keys():
distance = np.abs(symbol[0] + symbol[1]*1j - constellation_point)
if distance < min_distance:
min_distance = distance
closest_constellation_point = constellation_point
此代码块尝试计算最接近 8 位符号的 4 位星座点。您应该独立解调每个 4 位,然后连接结果。
def demodulate_4bits_signal(signal):
constellation = {
complex(-6, 6): 0, complex(-6, 2): 1, complex(-6, -6): 2,
complex(-6, -2): 3, complex(-2, 6): 4, complex(-2, 2): 5,
complex(-2, -6): 6, complex(-2, -2): 7, complex(6, 6): 8,
complex(6, 2): 9, complex(6, -6): 10, complex(6, -2): 11,
complex(2, 6): 12, complex(2, 2): 13, complex(2, -6): 14,
complex(2, -2): 15
}
min_distance = float('inf')
for constellation_point in constellation.keys():
distance = np.abs(signal - constellation_point)
if distance < min_distance:
min_distance = distance
closest_constellation_point = constellation_point
return constellation[closest_constellation_point]
def demodulate_symbol(symbol):
head, tail = symbol
return (demodulate_4bits_signal(head) << 4) + demodulate_4bits_signal(tail)
正如评论中所指出的,我们应该首先在没有噪音的情况下进行测试:
random_message = generate_random_string(50)
sent_signal = transmitter(random_message)
reconstructed_message = receiver(sent_signal)
print("Original message:", random_message)
print("Reconstructed message:", reconstructed_message)
成功了吗?
Original message: UZWE}jeD-aw2<(7w"JZ:+J2_H$LQdN6JYO5PfX<aAIRdSi_T>W
Reconstructed message: UZWE}jeD-aw2<(7w"JZ:+J2_H$LQdN6JYO5PfX<aAIRdSi_T>W
至于噪声信号的重建,我担心您会为输入产生太多噪声。
print(received_signal - sent_signal)
[-4.00022976e+01 -40.j -3.99901691e+01 -40.j ... ]
罪魁祸首是
A = np.array([[11, 10], [10, 11]])
这会将您的信号放大至少 10 倍。
这个代码还需要吗?你的
Z
变量已经包含一些噪音,所以让我们尝试只使用这些噪音:
def channel(sent_signal, noise_factor=1):
# ...
Z = np.random.normal(0, sigma*noise_factor, size=(2*n,))
Y = x + Z.T
return Y
Original message: ...
Reconstructed message: ...
Similarity ratio: 0.26
有点糟糕,但是让我们尝试使用较小的噪声比:
Y = channel(X, noise_factor=0.25)
Original message: ...
Reconstructed message: ...
Similarity ratio: 0.82
这样更好。当noise_ratio=0.15时,你的算法能够完全重建你的信号:
Y = channel(X, noise_factor=0.15)
Original message: ...
Reconstructed message: ...
Similarity ratio: 1.0
string.encode
直接操作字节,而不是将你的 ASCII 字符串转换为一串位,每个字节占用一个完整的字符。这非常繁重并且容易出错,因为您想将它们作为位而不是字符进行操作。请注意,我的代码片段旨在与您的代码片段相匹配。它们不应该被视为干净编码的例子。