From 0262a27f2c7cfe9223aab9bd946f06d60487936c Mon Sep 17 00:00:00 2001 From: appellet Date: Tue, 27 May 2025 11:32:50 +0200 Subject: [PATCH] doc: add README --- README.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 94 ++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 153 insertions(+), 35 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c20196a --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# Communication System README + +This repository contains a Python-based transmitter/receiver system for sending 40-character text messages over a simulated noisy channel (local or remote server). + +--- + +## Prerequisites + +* Python 3.7+ +* NumPy + +Install dependencies: + +```bash +pip install numpy +``` + +--- + +## Files + +* `encoder.py` — builds codebook and encodes messages. +* `decoder.py` — decodes received samples back into text. +* `channel.py` — local channel simulator. +* `client.py` — client stub to send samples to the remote server. +* `main.py` — main driver: wraps encode → channel → decode, plus performance testing. + +--- + +## Usage + +All commands assume you are in the project root directory. + +### 1. Test locally for 1 trial + +```bash +python3 main.py \ + --message "This is a 40-character test message...." \ + --trials 1 \ + --mode local +``` + +### 2. Test locally for 500 trials + +```bash +python3 main.py \ + --message "This is a 40-character test message...." \ + --trials 500 \ + --mode local +``` + +### 3. Test on the remote server (1 trial) + +This will write `input.txt` and `output.txt` in your working directory. + +```bash +python3 main.py \ + --message "This is a 40-character test message...." \ + --trials 1 \ + --mode server \ + --input_file input.txt \ + --output_file output.txt \ + --hostname iscsrv72.epfl.ch \ + --port 80 +``` + +> After running, `input.txt` contains your transmitted samples, and `output.txt` contains the noisy output from the server. + +--- + +## Manual decoding of server output + +If you want to decode `output.txt` yourself: + +```bash +python3 - << 'EOF' +import numpy as np +import encoder, decoder + +# Load noisy samples +Y = np.loadtxt("output.txt") + +# Rebuild codebook +C = encoder.make_codebook(r=5, num_blocks=40) + +# Decode +msg = decoder.decode_blocks(Y, C) +print("Decoded message:", msg) +EOF +``` + +--- + +Feel free to adjust `--trials` or swap between `local` and `server` modes as needed. diff --git a/main.py b/main.py index 783607c..4e4cb9a 100644 --- a/main.py +++ b/main.py @@ -7,36 +7,52 @@ import encoder import decoder import channel import subprocess -import tempfile import pathlib import os +import tempfile +# Global paths for debugging +INPUT_FILE = None +OUTPUT_FILE = None def transmit(msg, C): - """ + ''' Transmitter: encodes the message into real-valued samples using the codebook C. - """ + ''' return encoder.encode_message(msg, C) def receive_local(c): - """ + ''' Sends the samples through the local channel simulation. - """ + ''' return channel.channel(c) def receive_server(c, hostname, port): - """ - Sends the samples to the remote server via client.py and retrieves the output. - """ - # Write input samples to a temporary file - with tempfile.NamedTemporaryFile(suffix='.txt', delete=False) as in_f: - np.savetxt(in_f.name, c) - in_name = in_f.name - # Prepare output file - out_fd, out_name = tempfile.mkstemp(suffix='.txt') - os.close(out_fd) + ''' + Sends the samples to the remote server via client.py. If INPUT_FILE and/or + OUTPUT_FILE are set, uses those filenames (and preserves them); otherwise uses temporary files. + ''' + global INPUT_FILE, OUTPUT_FILE + # Determine input file path + if INPUT_FILE: + in_name = INPUT_FILE + np.savetxt(in_name, c) + delete_in = False + else: + with tempfile.NamedTemporaryFile(suffix='.txt', delete=False) as in_f: + np.savetxt(in_f.name, c) + in_name = in_f.name + delete_in = True + # Determine output file path + if OUTPUT_FILE: + out_name = OUTPUT_FILE + delete_out = False + else: + out_fd, out_name = tempfile.mkstemp(suffix='.txt') + os.close(out_fd) + delete_out = True # Invoke client.py cmd = [ sys.executable, @@ -50,30 +66,31 @@ def receive_server(c, hostname, port): subprocess.run(cmd, check=True) Y = np.loadtxt(out_name) finally: - # Clean up temp files - os.remove(in_name) - os.remove(out_name) + if delete_in and os.path.exists(in_name): + os.remove(in_name) + if delete_out and os.path.exists(out_name): + os.remove(out_name) return Y def receive(c, mode, hostname, port): - """ + ''' Wrapper to choose local or server channel. - """ + ''' if mode == 'local': return receive_local(c) elif mode == 'server': return receive_server(c, hostname, port) else: - raise ValueError("Mode must be 'local' or 'server'") + raise ValueError('Mode must be \'local\' or \'server\'') def test_performance(msg, num_trials, mode, hostname, port): - """ + ''' Runs num_trials transmissions of msg through the specified channel and reports accuracy. - """ + ''' if len(msg) != 40: - raise ValueError("Message must be exactly 40 characters.") + raise ValueError('Message must be exactly 40 characters.') # Build codebook for 64 symbols, 40 blocks C = encoder.make_codebook(r=5, num_blocks=40) successes = 0 @@ -88,27 +105,34 @@ def test_performance(msg, num_trials, mode, hostname, port): successes += 1 pct = successes / num_trials * 100 # Display results - print(f"Message: {msg}") - print(f"Trials: {num_trials}") - print(f"Mode: {mode}") - print(f"Correct decodings: {successes}") - print(f"Accuracy: {pct:.2f}%") + print(f'Message: {msg}') + print(f'Trials: {num_trials}') + print(f'Mode: {mode}') + print(f'Correct decodings: {successes}') + print(f'Accuracy: {pct:.2f}%') def parse_args(): - parser = argparse.ArgumentParser(description="Test communication system performance.") + parser = argparse.ArgumentParser(description='Test communication system performance.') parser.add_argument('--message', '-m', type=str, required=True, - help="40-character message to send.") - parser.add_argument('--trials', '-n', type=int, default=1, - help="Number of trials.") + help='40-character message to send.') + parser.add_argument('--trials', '-n', type=int, default=200, + help='Number of trials.') parser.add_argument('--mode', choices=['local','server'], default='local', help="Channel mode: 'local' or 'server'.") parser.add_argument('--hostname', type=str, default='iscsrv72.epfl.ch', - help="Server hostname for server mode.") + help='Server hostname for server mode.') parser.add_argument('--port', type=int, default=80, - help="Server port for server mode.") + help='Server port for server mode.') + parser.add_argument('--input_file', '-i', type=str, default=None, + help='Path to write server input samples (input.txt).') + parser.add_argument('--output_file', '-o', type=str, default=None, + help='Path to write server output samples (output.txt).') return parser.parse_args() if __name__ == '__main__': args = parse_args() + # Set global paths + INPUT_FILE = args.input_file + OUTPUT_FILE = args.output_file test_performance(args.message, args.trials, args.mode, args.hostname, args.port)