feat: improve results basing on theory
This commit is contained in:
parent
c4e3392d3e
commit
bbcf5d5fe7
10 changed files with 113 additions and 126 deletions
18
codebook.py
Normal file
18
codebook.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# codebook.py
|
||||
import numpy as np
|
||||
|
||||
def generate_Br(r):
|
||||
if r == 0:
|
||||
return np.array([[1]])
|
||||
M = generate_Br(r - 1)
|
||||
top = np.hstack((M, M))
|
||||
bottom = np.hstack((M, -M))
|
||||
return np.vstack((top, bottom))
|
||||
|
||||
def construct_codebook(r, Eb):
|
||||
Br = generate_Br(r)
|
||||
n = 2 * Br.shape[1] # Since codeword is [Br | Br]
|
||||
m = Br.shape[0] # Number of messages
|
||||
alpha = Eb * 2 / ((1 + 10) * n) # G=10
|
||||
codebook = np.sqrt(alpha) * np.hstack((Br, Br))
|
||||
return codebook, n, m, alpha
|
28
decoder.py
Normal file
28
decoder.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
# decoder.py
|
||||
import numpy as np
|
||||
from utils import index_to_char
|
||||
from codebook import construct_codebook
|
||||
|
||||
def decode_message(Y, codebook):
|
||||
Y1, Y2 = Y[::2], Y[1::2]
|
||||
scores = []
|
||||
for i, c in enumerate(codebook):
|
||||
c1, c2 = c[::2], c[1::2]
|
||||
s1 = np.sqrt(10) * np.dot(Y1, c1) + np.dot(Y2, c2)
|
||||
s2 = np.dot(Y1, c1) + np.sqrt(10) * np.dot(Y2, c2)
|
||||
scores.append(max(s1, s2))
|
||||
return np.argmax(scores)
|
||||
|
||||
def signal_to_text(Y, codebook, r=6):
|
||||
_, n, _, _ = construct_codebook(r, 1)
|
||||
seg_len = n
|
||||
text = ''
|
||||
for i in range(40):
|
||||
seg = Y[i * seg_len:(i + 1) * seg_len]
|
||||
index = decode_message(seg, codebook)
|
||||
if index in index_to_char:
|
||||
text += index_to_char[index]
|
||||
else:
|
||||
text += '?' # Unknown character
|
||||
return text
|
11
encoder.py
Normal file
11
encoder.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
import numpy as np
|
||||
from codebook import construct_codebook
|
||||
from utils import char_to_index, normalize_energy
|
||||
|
||||
def text_to_signal(text, r=9, Eb=4):
|
||||
assert len(text) == 40, "Message must be exactly 40 characters."
|
||||
codebook, n, m, alpha = construct_codebook(r, Eb)
|
||||
msg_indices = [char_to_index[c] for c in text]
|
||||
signal = np.concatenate([codebook[i] for i in msg_indices])
|
||||
signal = normalize_energy(signal, energy_limit=2000)
|
||||
return signal, codebook
|
28
main.py
28
main.py
|
@ -1,28 +0,0 @@
|
|||
import numpy as np
|
||||
from transmitter import transmitter
|
||||
from receiver import receiver
|
||||
from channel import channel
|
||||
|
||||
# Define the alphabet
|
||||
alphabet = (
|
||||
'abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789 .'
|
||||
)
|
||||
|
||||
# Example 40-character message
|
||||
test_message = 'ERROR 404. Motivation not found. Retry .'
|
||||
|
||||
# Transmit
|
||||
x = transmitter(test_message)
|
||||
|
||||
# Simulate channel
|
||||
Y = channel(x)
|
||||
|
||||
# Receive
|
||||
decoded_message = receiver(Y)
|
||||
|
||||
# Print results
|
||||
print(f"Original message: {test_message}")
|
||||
print(f"Decoded message: {decoded_message}")
|
||||
print(f"Bit error rate: {(np.array(list(test_message)) != np.array(list(decoded_message))).mean():.4f}")
|
|
@ -1 +0,0 @@
|
|||
Hello this is my message with 40 chars!
|
42
receiver.py
42
receiver.py
|
@ -1,42 +0,0 @@
|
|||
import numpy as np
|
||||
|
||||
def bits_to_text(bits, alphabet):
|
||||
char_to_idx = {char: idx for idx, char in enumerate(alphabet)}
|
||||
idx_to_char = {idx: char for char, idx in char_to_idx.items()}
|
||||
text = ''
|
||||
for i in range(0, len(bits), 6):
|
||||
idx = int(''.join(map(str, bits[i:i + 6])), 2)
|
||||
text += idx_to_char.get(idx, '?')
|
||||
return text
|
||||
|
||||
def receiver(Y):
|
||||
G = 10 # Match channel.py
|
||||
sigma2 = 10
|
||||
alphabet = (
|
||||
'abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789 .'
|
||||
)
|
||||
if Y.size != 480:
|
||||
raise ValueError("Received signal must have length 480.")
|
||||
n = 480
|
||||
bits = np.zeros(240, dtype=int)
|
||||
E = 4
|
||||
|
||||
# Estimate channel state
|
||||
even_energy = np.mean(np.abs(Y[::2]))
|
||||
odd_energy = np.mean(np.abs(Y[1::2]))
|
||||
# If even indices have higher energy, likely s=1; else s=2
|
||||
s_hat = 1 if even_energy > odd_energy else 2
|
||||
|
||||
for i in range(240):
|
||||
y_even = Y[2 * i]
|
||||
y_odd = Y[2 * i + 1]
|
||||
if s_hat == 1:
|
||||
llr = (y_even * np.sqrt(E) * np.sqrt(G) + y_odd * np.sqrt(E)) / sigma2
|
||||
else:
|
||||
llr = (y_even * np.sqrt(E) + y_odd * np.sqrt(E) * np.sqrt(G)) / sigma2
|
||||
bits[i] = 1 if llr < 0 else 0
|
||||
|
||||
text = bits_to_text(bits, alphabet)
|
||||
return text
|
17
test_local.py
Normal file
17
test_local.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from encoder import text_to_signal
|
||||
from decoder import signal_to_text
|
||||
from channel import channel
|
||||
|
||||
def test_local():
|
||||
message = "This is the end of the PDC course. Good "
|
||||
x, codebook = text_to_signal(message, r=9, Eb=5)
|
||||
y = channel(x)
|
||||
decoded = signal_to_text(y, codebook, r=9)
|
||||
|
||||
print(f"Original: {message}")
|
||||
print(f"Decoded : {decoded}")
|
||||
errors = sum(1 for a, b in zip(message, decoded) if a != b)
|
||||
print(f"Character errors: {errors}/40")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_local()
|
28
test_server.py
Normal file
28
test_server.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import subprocess
|
||||
import numpy as np
|
||||
from encoder import text_to_signal
|
||||
from decoder import signal_to_text
|
||||
|
||||
def test_server():
|
||||
message = "HelloWorld123 ThisIsATestMessage12345678"
|
||||
x, codebook = text_to_signal(message, r=6, Eb=1)
|
||||
np.savetxt("input.txt", x, fmt="%.10f")
|
||||
|
||||
subprocess.run([
|
||||
"python3", "client.py",
|
||||
"--input_file", "input.txt",
|
||||
"--output_file", "output.txt",
|
||||
"--srv_hostname", "iscsrv72.epfl.ch",
|
||||
"--srv_port", "80"
|
||||
])
|
||||
|
||||
y = np.loadtxt("output.txt")
|
||||
decoded = signal_to_text(y, codebook, r=6)
|
||||
|
||||
print(f"Original: {message}")
|
||||
print(f"Decoded : {decoded}")
|
||||
errors = sum(1 for a, b in zip(message, decoded) if a != b)
|
||||
print(f"Character errors: {errors}/40")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_server()
|
|
@ -1,55 +0,0 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
def text_to_bits(text, alphabet):
|
||||
"""Convert text to binary bits based on alphabet indexing."""
|
||||
char_to_idx = {char: idx for idx, char in enumerate(alphabet)}
|
||||
bits = []
|
||||
for char in text:
|
||||
idx = char_to_idx[char]
|
||||
# Convert index to 6-bit binary string, padded with zeros
|
||||
bits.extend([int(b) for b in format(idx, '06b')])
|
||||
return np.array(bits)
|
||||
|
||||
|
||||
def transmitter(text):
|
||||
"""
|
||||
Convert a 40-character text message to a signal vector x.
|
||||
|
||||
Parameters:
|
||||
text (str): Input text of 40 characters from the given alphabet.
|
||||
|
||||
Returns:
|
||||
np.ndarray: Signal vector x of length 480 with energy <= 2000.
|
||||
"""
|
||||
# Define the alphabet
|
||||
alphabet = (
|
||||
'abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789 .'
|
||||
)
|
||||
|
||||
# Input validation
|
||||
if len(text) != 40 or not all(c in alphabet for c in text):
|
||||
raise ValueError("Text must be 40 characters from the given alphabet.")
|
||||
|
||||
# Convert text to 240 bits (40 chars * 6 bits/char)
|
||||
bits = text_to_bits(text, alphabet)
|
||||
|
||||
# BPSK modulation: 0 -> +sqrt(E), 1 -> -sqrt(E)
|
||||
E = 4 # Energy per bit
|
||||
symbols = np.sqrt(E) * (1 - 2 * bits) # Map 0 to +sqrt(E), 1 to -sqrt(E)
|
||||
|
||||
# Create signal x: each bit is sent twice (odd and even indices)
|
||||
n = 480 # Block length
|
||||
x = np.zeros(n)
|
||||
for i in range(240):
|
||||
x[2 * i] = symbols[i] # Even index (2i)
|
||||
x[2 * i + 1] = symbols[i] # Odd index (2i+1)
|
||||
|
||||
# Verify energy constraint
|
||||
energy = np.sum(x ** 2)
|
||||
if energy > 2000:
|
||||
raise ValueError(f"Signal energy {energy} exceeds limit of 2000.")
|
||||
|
||||
return x
|
11
utils.py
Normal file
11
utils.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
alphabet = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 .")
|
||||
alphabet_size = len(alphabet)
|
||||
char_to_index = {c: i for i, c in enumerate(alphabet)}
|
||||
index_to_char = {i: c for i, c in enumerate(alphabet)}
|
||||
|
||||
import numpy as np
|
||||
|
||||
def normalize_energy(signal, energy_limit=2000):
|
||||
norm = np.sqrt(np.sum(signal ** 2))
|
||||
scale = np.sqrt(energy_limit) / norm
|
||||
return signal * scale
|
Loading…
Add table
Reference in a new issue