fix: a good version

This commit is contained in:
appellet 2025-05-20 19:17:08 +02:00
parent f41309e21b
commit 3ef68b4380
2 changed files with 111 additions and 103 deletions

View file

@ -1,59 +1,69 @@
import numpy as np import numpy as np
ALPHABET = (
list('abcdefghijklmnopqrstuvwxyz') + def bits_to_text(bits, alphabet):
list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') + """Convert binary bits to text using the alphabet."""
list('0123456789') + 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):
# Convert 6 bits to an integer
idx = int(''.join(map(str, bits[i:i + 6])), 2)
text += idx_to_char.get(idx, '?') # Default to '?' if index invalid
return text
def receiver(Y):
"""
Decode the received signal Y to a 40-character text message.
Parameters:
Y (np.ndarray): Received signal of length 480.
Returns:
str: Decoded 40-character text message.
"""
# Define constants
G = 10 # Power gain
sigma2 = 10 # Noise variance
alphabet = (
'abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'0123456789 .'
) )
def get_hadamard(n): # Input validation
assert (n & (n - 1) == 0), "Hadamard order must be power of 2" if Y.size != 480:
H = np.array([[1]]) raise ValueError("Received signal must have length 480.")
while H.shape[0] < n:
H = np.block([
[ H, H],
[ H, -H]
])
return H
def decode_signal(signal, alphabet=ALPHABET): n = 480
code_length = 64 bits = np.zeros(240, dtype=int)
n_chars = len(signal) // code_length E = 4 # Energy per bit, must match transmitter
H = get_hadamard(64)
scale = 1 / np.sqrt(code_length)
codebook = H * scale
decoded = [] # Process each bit (sent at indices 2i and 2i+1)
for i in range(n_chars): for i in range(240):
y = signal[i*code_length : (i+1)*code_length] y_odd = Y[2 * i] # Even index in Y (0-based)
# The channel may have applied sqrt(10) gain to odds or evens y_even = Y[2 * i + 1] # Odd index in Y
# We don't know which, so try both options and pick best
y_even = np.array(y)
y_even[::2] /= np.sqrt(10)
y_odd = np.array(y)
y_odd[1::2] /= np.sqrt(10)
# Try decoding both hypotheses
scores_even = codebook @ y_even
scores_odd = codebook @ y_odd
idx_even = np.argmax(scores_even)
idx_odd = np.argmax(scores_odd)
score_even = np.max(scores_even)
score_odd = np.max(scores_odd)
idx_best = idx_even if score_even > score_odd else idx_odd
decoded.append(alphabet[idx_best])
return ''.join(decoded)
def main(): # LLR for channel 1: odd indices have gain sqrt(G), even have gain 1
import argparse # H0: bit = 0 (sent +sqrt(E)), H1: bit = 1 (sent -sqrt(E))
parser = argparse.ArgumentParser(description="Receiver: decode y.txt to recovered message for PDC Project.") llr_ch1 = (
parser.add_argument('--input_file', type=str, required=True, help="Received y.txt from channel/server") (y_odd * np.sqrt(E) * np.sqrt(G) / sigma2) + # Odd index term
parser.add_argument('--output_file', type=str, required=True, help="Text file for the decoded message") (y_even * np.sqrt(E) / sigma2) # Even index term
args = parser.parse_args() )
y = np.loadtxt(args.input_file)
decoded = decode_signal(y)
with open(args.output_file, 'w') as f:
f.write(decoded)
if __name__ == '__main__': # LLR for channel 2: even indices have gain sqrt(G), odd have gain 1
main() llr_ch2 = (
(y_odd * np.sqrt(E) / sigma2) + # Odd index term
(y_even * np.sqrt(E) * np.sqrt(G) / sigma2) # Even index term
)
# Combine LLRs (assuming equal prior probabilities for both channels)
llr = 0.5 * (llr_ch1 + llr_ch2)
# Decode bit: LLR > 0 implies bit = 0, LLR < 0 implies bit = 1
bits[i] = 1 if llr < 0 else 0
# Convert bits to text
text = bits_to_text(bits, alphabet)
return text

View file

@ -1,57 +1,55 @@
import numpy as np import numpy as np
import sys
# Character Set and coding
ALPHABET = ( def text_to_bits(text, alphabet):
list('abcdefghijklmnopqrstuvwxyz') + """Convert text to binary bits based on alphabet indexing."""
list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') + char_to_idx = {char: idx for idx, char in enumerate(alphabet)}
list('0123456789') + 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 .'
) )
def get_hadamard(n): # Input validation
assert (n & (n - 1) == 0), "Hadamard order must be power of 2" if len(text) != 40 or not all(c in alphabet for c in text):
H = np.array([[1]]) raise ValueError("Text must be 40 characters from the given alphabet.")
while H.shape[0] < n:
H = np.block([
[ H, H],
[ H, -H]
])
return H
def encode_message(msg, alphabet=ALPHABET): # Convert text to 240 bits (40 chars * 6 bits/char)
msg = msg.strip() bits = text_to_bits(text, alphabet)
if len(msg) != 40:
raise Exception("Message must be exactly 40 characters!") # BPSK modulation: 0 -> +sqrt(E), 1 -> -sqrt(E)
# Get Hadamard codes E = 4 # Energy per bit
H = get_hadamard(64) symbols = np.sqrt(E) * (1 - 2 * bits) # Map 0 to +sqrt(E), 1 to -sqrt(E)
code_length = 64
# Normalize so signal energy stays bounded # Create signal x: each bit is sent twice (odd and even indices)
# Each row has norm sqrt(64) = 8, so scale down by 1/8 n = 480 # Block length
scale = 1 / np.sqrt(code_length) x = np.zeros(n)
signals = [] for i in range(240):
for c in msg: x[2 * i] = symbols[i] # Even index (2i)
idx = alphabet.index(c) x[2 * i + 1] = symbols[i] # Odd index (2i+1)
signals.append(H[idx] * scale)
signal = np.concatenate(signals) # Verify energy constraint
# Energy check (should be << 2000) energy = np.sum(x ** 2)
assert signal.shape[0] == 2560
energy = np.sum(signal ** 2)
if energy > 2000: if energy > 2000:
raise Exception("Signal energy above allowed!") raise ValueError(f"Signal energy {energy} exceeds limit of 2000.")
return signal
def main(): return x
import argparse
parser = argparse.ArgumentParser(description="Message to signal encoder for PDC Project")
parser.add_argument('--message_file', type=str, required=True, help="Text file with exactly 40 chars.")
parser.add_argument('--output_file', type=str, required=True, help="Output signal file for client.py.")
args = parser.parse_args()
with open(args.message_file, 'r') as f:
msg = f.read().strip()
x = encode_message(msg)
np.savetxt(args.output_file, x)
if __name__ == '__main__':
main()