feat: get 67% correctness
This commit is contained in:
parent
545d4d7768
commit
df631a2199
5 changed files with 95 additions and 83 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
|
|
|
@ -1,54 +0,0 @@
|
||||||
# evaluate_zero_error_ratio.py
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import numpy as np
|
|
||||||
from encoder import text_to_signal
|
|
||||||
from decoder import signal_to_text
|
|
||||||
from channel import channel
|
|
||||||
|
|
||||||
def zero_error_ratio(message: str, r: int, Eb: float, n_runs: int) -> float:
|
|
||||||
"""
|
|
||||||
Runs the encode–channel–decode pipeline n_runs times on the same message,
|
|
||||||
and returns the fraction of runs with 0 character errors.
|
|
||||||
"""
|
|
||||||
zero_count = 0
|
|
||||||
for _ in range(n_runs):
|
|
||||||
# encode
|
|
||||||
x, codebook = text_to_signal(message, r=r, Eb=Eb)
|
|
||||||
# pass through channel
|
|
||||||
y = channel(x)
|
|
||||||
# decode
|
|
||||||
decoded = signal_to_text(y, codebook, r=r)
|
|
||||||
# count errors
|
|
||||||
errors = sum(1 for a, b in zip(message, decoded) if a != b)
|
|
||||||
if errors == 0:
|
|
||||||
zero_count += 1
|
|
||||||
return zero_count / n_runs
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Estimate the zero-error run ratio over n trials."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--message", type=str, required=True,
|
|
||||||
help="The 40-char message to test."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--r", type=int, default=6,
|
|
||||||
help="Codebook parameter r (default: 6)."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--Eb", type=float, default=3.0,
|
|
||||||
help="Energy per bit Eb (default: 3)."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--n", type=int, default=1000,
|
|
||||||
help="Number of runs (default: 1000)."
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if len(args.message) != 40:
|
|
||||||
raise ValueError("Message must be exactly 40 characters long.")
|
|
||||||
|
|
||||||
ratio = zero_error_ratio(args.message, args.r, args.Eb, args.n)
|
|
||||||
print(f"Zero-error runs: {ratio:.3%} ({ratio:.4f} of {args.n})")
|
|
58
main.py
Normal file
58
main.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# main.py
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import socket
|
||||||
|
import numpy as np
|
||||||
|
import channel_helper as ch
|
||||||
|
from encoder import make_codebook, encode_message
|
||||||
|
from decoder import decode_blocks, count_errors
|
||||||
|
from channel import channel as external_channel
|
||||||
|
|
||||||
|
|
||||||
|
def send_and_recv(x: np.ndarray, host: str, port: int) -> np.ndarray:
|
||||||
|
"""Send samples x to server and receive output via TCP"""
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.connect((host, port))
|
||||||
|
header = b'0' + b'dUV'
|
||||||
|
ch.send_msg(sock, header, x)
|
||||||
|
_, Y = ch.recv_msg(sock)
|
||||||
|
return Y
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser(description="PDC Tx/Rx local or server")
|
||||||
|
p.add_argument("--message", required=True, help="40-character message to send")
|
||||||
|
p.add_argument("--srv_hostname", help="Server hostname")
|
||||||
|
p.add_argument("--srv_port", type=int, help="Server port")
|
||||||
|
p.add_argument("--local", action='store_true', help="Use local channel simulation")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
msg = args.message
|
||||||
|
if len(msg) != 40:
|
||||||
|
raise ValueError("Message must be exactly 40 characters.")
|
||||||
|
num_blocks = len(msg)
|
||||||
|
C = make_codebook(r=5, num_blocks=num_blocks, margin=0.95)
|
||||||
|
x = encode_message(msg, C)
|
||||||
|
print(f"→ Total samples = {x.size}, total energy = {np.sum(x*x):.1f}")
|
||||||
|
|
||||||
|
if args.local:
|
||||||
|
print("-- Local simulation mode --")
|
||||||
|
Y = external_channel(x)
|
||||||
|
else:
|
||||||
|
if not args.srv_hostname or not args.srv_port:
|
||||||
|
raise ValueError("Must specify --srv_hostname and --srv_port unless --local")
|
||||||
|
Y = send_and_recv(x, args.srv_hostname, args.srv_port)
|
||||||
|
|
||||||
|
msg_hat = decode_blocks(Y, C)
|
||||||
|
print(f"↓ Decoded message: {msg_hat}")
|
||||||
|
|
||||||
|
errors = count_errors(msg, msg_hat)
|
||||||
|
print(f"Errors: {len(errors)} / {len(msg)} characters differ")
|
||||||
|
if errors:
|
||||||
|
for i, o, e in errors:
|
||||||
|
print(f" Pos {i}: sent '{o}' but got '{e}'")
|
||||||
|
else:
|
||||||
|
print("✔️ No decoding errors!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
37
performance_local.py
Normal file
37
performance_local.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# performance_local.py
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
from encoder import make_codebook, encode_message, ALPHABET
|
||||||
|
from decoder import decode_blocks, count_errors
|
||||||
|
from channel import channel
|
||||||
|
|
||||||
|
|
||||||
|
def random_message(length):
|
||||||
|
return ''.join(random.choice(ALPHABET) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Monte Carlo evaluation over local channel")
|
||||||
|
parser.add_argument("--num", type=int, required=True, help="Number of trials")
|
||||||
|
parser.add_argument("--r", type=int, default=5, help="Hadamard order (default 5)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
num_trials = args.num
|
||||||
|
successes = 0
|
||||||
|
|
||||||
|
for _ in range(num_trials):
|
||||||
|
msg = random_message(40)
|
||||||
|
C = make_codebook(r=args.r, num_blocks=len(msg))
|
||||||
|
x = encode_message(msg, C)
|
||||||
|
Y = channel(x)
|
||||||
|
msg_hat = decode_blocks(Y, C)
|
||||||
|
if msg_hat == msg:
|
||||||
|
successes += 1
|
||||||
|
|
||||||
|
ratio = successes / num_trials
|
||||||
|
print(f"Correctly decoded messages: {successes}/{num_trials} ({ratio:.2%})")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
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