Compare commits
No commits in common. "0c19f7ce4042caa00197e5bb87d3935b8891b195" and "8af74ffbd30f39d3026bbe165adad67022797c8f" have entirely different histories.
0c19f7ce40
...
8af74ffbd3
9 changed files with 125 additions and 113 deletions
18
codebook.py
18
codebook.py
|
@ -1,18 +0,0 @@
|
||||||
# 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
28
decoder.py
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
# 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
11
encoder.py
|
@ -1,11 +0,0 @@
|
||||||
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
Normal file
28
main.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
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
Normal file
42
receiver.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
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
|
|
@ -1,17 +0,0 @@
|
||||||
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()
|
|
|
@ -1,28 +0,0 @@
|
||||||
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()
|
|
55
transmitter.py
Normal file
55
transmitter.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
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
11
utils.py
|
@ -1,11 +0,0 @@
|
||||||
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