Compare commits
2 commits
8af74ffbd3
...
0c19f7ce40
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0c19f7ce40 | ||
![]() |
bbcf5d5fe7 |
9 changed files with 113 additions and 125 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}")
|
|
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