fix: a good version
This commit is contained in:
parent
f41309e21b
commit
3ef68b4380
2 changed files with 111 additions and 103 deletions
114
receiver.py
114
receiver.py
|
@ -1,59 +1,69 @@
|
|||
import numpy as np
|
||||
|
||||
ALPHABET = (
|
||||
list('abcdefghijklmnopqrstuvwxyz') +
|
||||
list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') +
|
||||
list('0123456789') +
|
||||
[' ', '.']
|
||||
)
|
||||
|
||||
def get_hadamard(n):
|
||||
assert (n & (n - 1) == 0), "Hadamard order must be power of 2"
|
||||
H = np.array([[1]])
|
||||
while H.shape[0] < n:
|
||||
H = np.block([
|
||||
[ H, H],
|
||||
[ H, -H]
|
||||
])
|
||||
return H
|
||||
def bits_to_text(bits, alphabet):
|
||||
"""Convert binary bits to text using the 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):
|
||||
# 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 decode_signal(signal, alphabet=ALPHABET):
|
||||
code_length = 64
|
||||
n_chars = len(signal) // code_length
|
||||
H = get_hadamard(64)
|
||||
scale = 1 / np.sqrt(code_length)
|
||||
codebook = H * scale
|
||||
|
||||
decoded = []
|
||||
for i in range(n_chars):
|
||||
y = signal[i*code_length : (i+1)*code_length]
|
||||
# The channel may have applied sqrt(10) gain to odds or evens
|
||||
# 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 receiver(Y):
|
||||
"""
|
||||
Decode the received signal Y to a 40-character text message.
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Receiver: decode y.txt to recovered message for PDC Project.")
|
||||
parser.add_argument('--input_file', type=str, required=True, help="Received y.txt from channel/server")
|
||||
parser.add_argument('--output_file', type=str, required=True, help="Text file for the decoded message")
|
||||
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)
|
||||
Parameters:
|
||||
Y (np.ndarray): Received signal of length 480.
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Returns:
|
||||
str: Decoded 40-character text message.
|
||||
"""
|
||||
# Define constants
|
||||
G = 10 # Power gain
|
||||
sigma2 = 10 # Noise variance
|
||||
alphabet = (
|
||||
'abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789 .'
|
||||
)
|
||||
|
||||
# Input validation
|
||||
if Y.size != 480:
|
||||
raise ValueError("Received signal must have length 480.")
|
||||
|
||||
n = 480
|
||||
bits = np.zeros(240, dtype=int)
|
||||
E = 4 # Energy per bit, must match transmitter
|
||||
|
||||
# Process each bit (sent at indices 2i and 2i+1)
|
||||
for i in range(240):
|
||||
y_odd = Y[2 * i] # Even index in Y (0-based)
|
||||
y_even = Y[2 * i + 1] # Odd index in Y
|
||||
|
||||
# LLR for channel 1: odd indices have gain sqrt(G), even have gain 1
|
||||
# H0: bit = 0 (sent +sqrt(E)), H1: bit = 1 (sent -sqrt(E))
|
||||
llr_ch1 = (
|
||||
(y_odd * np.sqrt(E) * np.sqrt(G) / sigma2) + # Odd index term
|
||||
(y_even * np.sqrt(E) / sigma2) # Even index term
|
||||
)
|
||||
|
||||
# LLR for channel 2: even indices have gain sqrt(G), odd have gain 1
|
||||
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
|
100
transmitter.py
100
transmitter.py
|
@ -1,57 +1,55 @@
|
|||
import numpy as np
|
||||
import sys
|
||||
|
||||
# Character Set and coding
|
||||
ALPHABET = (
|
||||
list('abcdefghijklmnopqrstuvwxyz') +
|
||||
list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') +
|
||||
list('0123456789') +
|
||||
[' ', '.']
|
||||
)
|
||||
|
||||
def get_hadamard(n):
|
||||
assert (n & (n - 1) == 0), "Hadamard order must be power of 2"
|
||||
H = np.array([[1]])
|
||||
while H.shape[0] < n:
|
||||
H = np.block([
|
||||
[ H, H],
|
||||
[ H, -H]
|
||||
])
|
||||
return H
|
||||
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 encode_message(msg, alphabet=ALPHABET):
|
||||
msg = msg.strip()
|
||||
if len(msg) != 40:
|
||||
raise Exception("Message must be exactly 40 characters!")
|
||||
# Get Hadamard codes
|
||||
H = get_hadamard(64)
|
||||
code_length = 64
|
||||
# Normalize so signal energy stays bounded
|
||||
# Each row has norm sqrt(64) = 8, so scale down by 1/8
|
||||
scale = 1 / np.sqrt(code_length)
|
||||
signals = []
|
||||
for c in msg:
|
||||
idx = alphabet.index(c)
|
||||
signals.append(H[idx] * scale)
|
||||
signal = np.concatenate(signals)
|
||||
# Energy check (should be << 2000)
|
||||
assert signal.shape[0] == 2560
|
||||
energy = np.sum(signal ** 2)
|
||||
|
||||
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 Exception("Signal energy above allowed!")
|
||||
return signal
|
||||
raise ValueError(f"Signal energy {energy} exceeds limit of 2000.")
|
||||
|
||||
def main():
|
||||
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()
|
||||
return x
|
Loading…
Add table
Reference in a new issue