From 4ba1c1eccffc836223a61da495590b85f1254bcf Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Mon, 27 May 2019 22:43:10 +0200 Subject: [PATCH 01/20] Continue receiver for parity check --- src/local_test.py | 9 +- src/main.py | 11 +- src/params.py | 10 +- src/preambles.py | 2 +- src/pulses.py | 4 + src/receiver.py | 281 ++++++++++++++++++++++++++++++++++++--------- src/transmitter.py | 191 ++++++++++++++++++++---------- 7 files changed, 374 insertions(+), 134 deletions(-) diff --git a/src/local_test.py b/src/local_test.py index e815950..5a3f9d6 100644 --- a/src/local_test.py +++ b/src/local_test.py @@ -309,4 +309,11 @@ def local_test(): # Intended for testing (to run the program, run main.py) if __name__ == "__main__": - local_test() + _, h = pulses.root_raised_cosine() + a = [1, 1] + samples = upfirdn(h, a, params.USF) + print(samples) + print(np.argmax(samples)) + print(len(samples)) + plot_helper.plot_complex_function(samples, "Test rrc") + diff --git a/src/main.py b/src/main.py index c9a8a67..7d58346 100644 --- a/src/main.py +++ b/src/main.py @@ -19,20 +19,11 @@ params.params_log() # Transmitter - symbols = transmitter.encoder(transmitter.message_to_ints(), mappings.choose_mapping()) + symbols = transmitter.encoder(mappings.choose_mapping()) _, h = pulses.root_raised_cosine() samples_to_send = transmitter.waveform_former(h, symbols) read_write.write_samples(samples_to_send) transmitter.send_samples() - # Wait for the user to press enter (in case the server is down for example) - sys.stdout = sys.__stdout__ - input("Press Enter to continue...") - if params.logs: - sys.stdout = log_file - # Receiver receiver.received_from_server() - - sys.stdout = sys.__stdout__ - input("Press Enter to end the transmission!") diff --git a/src/params.py b/src/params.py index f9f780c..185a48c 100644 --- a/src/params.py +++ b/src/params.py @@ -14,7 +14,7 @@ def choose_symbol_period(): # General variables logs = True -plots = False +plots = True input_message_file_path = "../data/input_text.txt" output_message_file_path = "../data/output_text.txt" @@ -40,15 +40,15 @@ def choose_symbol_period(): # --------------------------------------------- # Communication parameters -MAPPING = "qam" # mapping: qam or psk or pam for now +MAPPING = "qam" # mapping: qam or psk or pam for now (pam not well integrated yet (e.g SSB)) NORMALIZE_MAPPING = False # rather we normalize the mapping or not M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MODULATION_TYPE = 1 +MODULATION_TYPE = 3 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) -# 3 = parity check approach +# 3 = parity check approach (only works for M=4 here) MODULATION_TYPE_1_BANDWIDTH = 2000 MODULATION_TYPE_2_BANDWIDTH = 4000 MODULATION_TYPE_3_BANDWIDTH = 2000 @@ -60,7 +60,7 @@ def choose_symbol_period(): USF = int(np.ceil(T * Fs)) # up-sampling factor, i.e the number of zeros to add between any 2 symbols before # pulse shaping -SPAN = 4 * USF # size of our pulse in number of samples +SPAN = 20 * USF # size of our pulse in number of samples PREAMBLE_TYPE = "barker" # Type of preamble to generate (barker or random for now) BARKER_SEQUENCE_REPETITION = 1 # Number of repetitions of the barker sequence diff --git a/src/preambles.py b/src/preambles.py index 39a8560..5c5b022 100644 --- a/src/preambles.py +++ b/src/preambles.py @@ -18,7 +18,7 @@ def generate_preamble_symbols(n_symbols_to_send): else: raise ValueError('This preamble type does not exist yet... Hehehe') - if params.MAPPING == "qam" and not params.NORMALIZE_MAPPING: + if params.MAPPING == "qam" and not params.NORMALIZE_MAPPING and params.PREAMBLE_TYPE == "barker": # TODO: improve that if params.M == 16: read_write.write_preamble_symbols(preamble_symbols * 3) diff --git a/src/pulses.py b/src/pulses.py index 4840950..97746de 100644 --- a/src/pulses.py +++ b/src/pulses.py @@ -21,6 +21,7 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params rrc = np.zeros(SPAN) time_indices = (np.arange(SPAN) - SPAN / 2) * Ts sample_numbers = np.arange(SPAN) + index_0 = None if beta == 0: for n in sample_numbers: @@ -44,6 +45,8 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params if abs(t) == forbidden_value_t: rrc[n] = rrc_beta_forbidden_value elif beta == 1 or t == 0: + if t == 0: + index_0 = n rrc[n] = first_term * (np.cos((1 + beta) * pi * t / T) + second_term) / (1 - third_term * t ** 2) else: rrc[n] = first_term * \ @@ -57,6 +60,7 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params if params.logs: print("Root-raised-cosine:\nSPAN = {} samples, beta = {}, T = {} seconds, Fs = {} samples per second (Hz)" .format(SPAN, beta, T, Fs)) + print("Index where t = 0: {} ({} values left and {} values right)".format(index_0, index_0, index_0 - 1)) print("Normalized = {}".format(params.NORMALIZE_PULSE)) print("--------------------------------------------------------") if params.plots: diff --git a/src/receiver.py b/src/receiver.py index a3fdb8a..c2a3c9a 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -10,6 +10,7 @@ import read_write +# TODO: merge both methods def decoder(y, mapping): """ Map the received symbols to the closest symbols of our mapping @@ -89,9 +90,13 @@ def ints_to_message(ints): # TODO: Slice it in smaller methods def received_from_server(): + # Prepare the data ------------------------------------------------------------------------------------------------- + if params.logs: + print("Preparing the data...") # Load the input and output samples from their respective files input_samples = np.loadtxt(params.input_sample_file_path) - received_samples = np.loadtxt(params.output_sample_file_path) + # TODO: put output again + received_samples = np.loadtxt(params.input_sample_file_path) # Plot the input and output samples in Time domain and Frequency domain if params.plots: @@ -107,99 +112,263 @@ def received_from_server(): if params.plots: plot_helper.plot_complex_function(received_samples, "Received samples in time domain") plot_helper.fft_plot(received_samples, "Received samples in frequency domain", shift=True) + if params.logs: + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Find the frequency range that has been removed + # Find the frequency range that has been removed ------------------------------------------------------------------- + if params.logs: + print("Finding the frequency range that has been removed...") range_indices, removed_freq_range = fourier_helper.find_removed_freq_range(received_samples) if params.logs: print("Removed frequency range: {} (range {})".format(removed_freq_range, removed_freq_range + 1)) - # Choose a frequency for demodulation - if params.MODULATION_TYPE == 1: - if removed_freq_range == 0: - fc = np.mean(params.FREQ_RANGES[1]) - else: - fc = np.mean(params.FREQ_RANGES[0]) - elif params.MODULATION_TYPE == 2: - if removed_freq_range == 0 or removed_freq_range == 1: - fc = np.mean([params.FREQ_RANGES[2][0], params.FREQ_RANGES[3][1]]) + # Array of the form [True, True, False, True], where False means that the 3rd frequency range is removes here + frequency_ranges_available = [True if i != removed_freq_range else False for i in range(len(params.FREQ_RANGES))] + if params.logs: + print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ + + # Demodulation ----------------------------------------------------------------------------------------------------- + if params.logs: + print("Demodulation...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + if params.MODULATION_TYPE == 1: + fc = np.mean(params.FREQ_RANGES[frequency_ranges_available.index(True)]) + + # TODO: improve this in term of the frequency_ranges_available array above else: - fc = np.mean([params.FREQ_RANGES[0][0], params.FREQ_RANGES[2][1]]) + if removed_freq_range == 0 or removed_freq_range == 1: + fc = np.mean([params.FREQ_RANGES[2][0], params.FREQ_RANGES[3][1]]) + else: + fc = np.mean([params.FREQ_RANGES[0][0], params.FREQ_RANGES[2][1]]) + if params.logs: + print("Chosen fc for demodulation: {}".format(fc)) + + demodulated_samples = fourier_helper.demodulate(received_samples, fc) + if params.plots: + plot_helper.plot_complex_function(demodulated_samples, "Demodulated samples in Time domain") + plot_helper.fft_plot(demodulated_samples, "Demodulated samples in Frequency domain", shift=True) + + elif params.MODULATION_TYPE == 3: + demodulated_samples = [] + for i in range(len(params.FREQ_RANGES)): + if frequency_ranges_available[i]: + demodulated_samples.append(fourier_helper.demodulate(received_samples, np.mean(params.FREQ_RANGES[i]))) + else: + demodulated_samples.append([]) else: raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print("Chosen fc for demodulation: {}".format(fc)) - - # Demodulate the received samples to base-band - demodulated_samples = fourier_helper.demodulate(received_samples, fc) - if params.plots: - plot_helper.plot_complex_function(demodulated_samples, "Demodulated samples in Time domain") - plot_helper.fft_plot(demodulated_samples, "Demodulated samples in Frequency domain", shift=True) + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Match filter (i.e Low-pass) + # Low pass --------------------------------------------------------------------------------------------------------- + if params.logs: + print("Low passing...") _, h = pulses.root_raised_cosine() half_span_h = int(params.SPAN / 2) h_matched = np.conjugate(h[::-1]) - y = np.convolve(demodulated_samples, h_matched) - if params.plots: - plot_helper.plot_complex_function(y, "y in Time domain") - plot_helper.fft_plot(y, "y in Frequency domain", shift=True) - # Find the delay - delay = parameter_estim.ML_theta_estimation(demodulated_samples, preamble_samples=preamble_samples) + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + y = np.convolve(demodulated_samples, h_matched) + if params.plots: + plot_helper.plot_complex_function(y, "y in Time domain") + plot_helper.fft_plot(y, "y in Frequency domain", shift=True) + elif params.MODULATION_TYPE == 3: + y = [] + for i in range(len(demodulated_samples)): + if frequency_ranges_available[i]: + y.append(np.convolve(demodulated_samples[i], h_matched)) + else: + y.append([]) + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("Y shape:") + for i in range(len(y)): + print(np.shape(y[i])) + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ + + # Find the delay --------------------------------------------------------------------------------------------------- + if params.logs: + print("Finding the delay...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + delay = parameter_estim.ML_theta_estimation(y, preamble_samples) + elif params.MODULATION_TYPE == 3: + delays = [] + for i in range(len(y)): + if frequency_ranges_available[i]: + delays.append(parameter_estim.ML_theta_estimation(y[i], preamble_samples)) + delay = int(np.round(np.mean(delays))) + else: + raise ValueError('This modulation type does not exist yet... He he he') if params.logs: print("Delay: {} samples".format(delay)) print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Extract the preamble samples - preamble_samples_received = y[half_span_h + delay - 1:half_span_h + delay + len_preamble_samples - 1] + # Extract the preamble samples ------------------------------------------------------------------------------------- + if params.logs: + print("Extracting the preamble samples...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + preamble_samples_received = y[delay:delay + len_preamble_samples] + elif params.MODULATION_TYPE == 3: + preamble_samples_received = [] + for i in range(len(y)): + if frequency_ranges_available[i]: + preamble_samples_received.append(y[i][delay:delay + len_preamble_samples]) + preamble_samples_received = np.mean(preamble_samples_received, axis=0) + else: + raise ValueError('This modulation type does not exist yet... He he he') if params.plots: - plot_helper.two_simple_plots(preamble_samples_received.real, preamble_samples.real, - "Comparison between preamble samples received and preamble samples sent", - "received", "expected") + plot_helper.two_simple_plots(np.real(preamble_samples_received), np.real(preamble_samples), + "Preamble samples received vs preamble samples sent", "received", "expected") if params.logs: - print("Number of samples for the actual preamble: {}".format(len_preamble_samples)) - print("Number of samples for the received preamble: {}".format(len(preamble_samples_received))) + # print("Number of samples for the actual preamble: {} samples".format(len_preamble_samples)) + # print("Number of samples for the received preamble: {} samples".format(len(preamble_samples_received))) + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Compute the phase shift + # Compute the phase shift and the scaling factor ------------------------------------------------------------------- + if params.logs: + print("Computing the phase shift and the scaling factor...") + # Remove SPAN/2 samples in the end because there is still data there for the received preamble phase_shift_estim, scaling_factor = parameter_estim.ML_phase_scaling_estim( preamble_samples[:len_preamble_samples - half_span_h], preamble_samples_received[:len(preamble_samples_received) - half_span_h]) if params.logs: - print("Frequency offset: {}".format(phase_shift_estim)) + print("Phase shift: {}".format(phase_shift_estim)) print("Scaling factor: {}".format(scaling_factor)) + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Crop the samples (remove the delay, the preamble, and the ramp-up) - data_samples = y[half_span_h + delay + len_preamble_samples - half_span_h + params.USF - 1 - 1:] + # Crop the samples (remove the delay, the preamble, and adjust to the first relevant sample of data) --------------- + if params.logs: + print("Cropping the samples (removing the delay, the preamble, " + "and adjusting to the first relevant sample of data)...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + data_samples = y[delay + len_preamble_samples - half_span_h + params.USF - 1:] + if params.plots: + plot_helper.plot_complex_function(data_samples, "y after removing the delay, the preamble, and adjusting") + elif params.MODULATION_TYPE == 3: + data_samples = [] + for i in range(len(y)): + if frequency_ranges_available[i]: + data_samples.append(y[i][delay + len_preamble_samples - half_span_h + params.USF:]) + if params.plots: + j = 0 + for i in range(len(y)): + if frequency_ranges_available[i]: + plot_helper.plot_complex_function( + data_samples[j], "y[{}] after removing the delay, the preamble, and adjusting".format(i)) + j += 1 + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Find the second_preamble_index - second_preamble_index = parameter_estim.ML_theta_estimation(data_samples, preamble_samples=preamble_samples[::-1]) + # Find the second_preamble_index ----------------------------------------------------------------------------------- + if params.logs: + print("Finding the second preamble index...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + second_preamble_index = parameter_estim.ML_theta_estimation(data_samples, + preamble_samples=preamble_samples[::-1]) + elif params.MODULATION_TYPE == 3: + second_preamble_index = [] + j = 0 + for i in range(len(data_samples)): + if frequency_ranges_available[i]: + second_preamble_index.append(parameter_estim.ML_theta_estimation( + data_samples[j], preamble_samples=preamble_samples[::-1])) + j += 1 + second_preamble_index = int(np.round(np.mean(second_preamble_index))) + else: + raise ValueError('This modulation type does not exist yet... He he he') if params.logs: print("Second preamble index: {} samples".format(second_preamble_index)) print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Crop the samples (remove the garbage at the end) - data_samples = data_samples[:second_preamble_index + half_span_h - params.USF + 1] - if params.plots: - plot_helper.plot_complex_function(data_samples, "y after removing the delay, the preamble, and the ramp-up") + # Crop the samples (remove the garbage at the end) ----------------------------------------------------------------- + if params.logs: + print("Cropping the samples (removing the garbage at the end)...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + data_samples = data_samples[:second_preamble_index + half_span_h - params.USF + 1] + if params.plots: + plot_helper.plot_complex_function(data_samples, "y (only data)") + elif params.MODULATION_TYPE == 3: + for i in range(len(data_samples)): + data_samples[i] = data_samples[i][:second_preamble_index + half_span_h - params.USF + 1] + if params.plots: + for i in range(len(data_samples)): + plot_helper.plot_complex_function(data_samples[i], "y (only data)") + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Correct the phase shift on the data samples - data_samples = data_samples * np.exp(-1j * (phase_shift_estim - np.pi / 2)) + # Correct the phase shift on the data samples ---------------------------------------------------------------------- + if params.logs: + print("Correcting the phase shift and the scaling factor (not yet) on the data samples...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + data_samples = data_samples * np.exp(-1j * (phase_shift_estim - np.pi / 2)) + elif params.MODULATION_TYPE == 3: + for i in range(len(data_samples)): + data_samples[i] = data_samples[i] * np.exp(-1j * (phase_shift_estim - np.pi / 2)) + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Down-sample the samples to obtain the symbols - data_symbols = data_samples[::params.USF] + # Down-sample the samples to obtain the symbols -------------------------------------------------------------------- if params.logs: - print("Number of symbols received: {}".format(len(data_symbols))) - if params.plots: - plot_helper.plot_complex_function(data_symbols, "y without preamble") - plot_helper.plot_complex_symbols(data_symbols, "Symbols received", annotate=False) + print("Down-sampling...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + data_symbols = data_samples[::params.USF] + if params.logs: + print("Number of symbols received: {}".format(len(data_symbols))) + if params.plots: + plot_helper.plot_complex_function(data_symbols, "y without preamble") + plot_helper.plot_complex_symbols(data_symbols, "Symbols received", annotate=False) + elif params.MODULATION_TYPE == 3: + data_symbols = [] + for i in range(len(data_samples)): + data_symbols.append(data_samples[i][::params.USF]) + if params.logs: + print("Shape of the received symbols: {}".format(np.shape(data_symbols))) + if params.plots: + for i in range(len(data_symbols)): + plot_helper.plot_complex_function(data_symbols[i], "y without preamble") + plot_helper.plot_complex_symbols(data_symbols[i], "Symbols received", annotate=False) + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") + # ------------------------------------------------------------------------------------------------------------------ - # Decode the symbols - ints = decoder(data_symbols, np.asarray(mappings.choose_mapping())) - message_received = ints_to_message(ints) - read_write.write_message_received(message_received) + # Decode the symbols ----------------------------------------------------------------------------------------------- + if params.logs: + print("Decoding the symbols...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + ints = decoder(data_symbols, np.asarray(mappings.choose_mapping())) + message_received = ints_to_message(ints) + read_write.write_message_received(message_received) + # elif params.MODULATION_TYPE == 3: + # TODO + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") # Intended for testing (to run the program, run main.py) if __name__ == "__main__": received_from_server() + +# TODO: make sure the scaling factor, delay, phase shift must be the same for 3 freq ranges diff --git a/src/transmitter.py b/src/transmitter.py index 7bd4565..e4ba9f9 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -1,5 +1,6 @@ import subprocess - +import time +import sys import numpy as np from scipy.signal import upfirdn @@ -13,10 +14,11 @@ import read_write -def message_to_ints(): +def encoder(mapping): """ - Convert the message into an array of integers corresponding to the indices in the chosen mapping - :return: the mapping indices corresponding to our message + Encode a message into a sequence of symbols according to the given mapping + :param mapping: the mapping used for transmission + :return: the corresponding symbols for the message """ # Retrieve the message from file message_file = open(params.input_message_file_path) @@ -24,13 +26,14 @@ def message_to_ints(): print("Sent message:\n{}".format(message)) if params.logs: print("Length: {} characters".format(len(message))) + print("--------------------------------------------------------") # Retrieve the message as a sequences of binary bytes string_bytes = helper.string2bits(message) - # Next step is to re-arrange string_bytes in agreement with M. Indeed, with a symbol constellation of M points, - # we can only represent BITS_PER_SYMBOL=log2(M) bits per symbol. Thus, we want to re-structure string_bytes - # with BITS_PER_SYMBOL=log2(M) bits by row. + if params.logs: + print("Corresponding bytes:\n{}".format(string_bytes)) + print("--------------------------------------------------------") # Remove the most significant bit (0) as it is useless in ASCII (do not forget to put it again in the receiver!) new_bits = [b[1:] for b in string_bytes] @@ -38,41 +41,82 @@ def message_to_ints(): # Make a new string with these cropped bytes new_bits = ''.join(new_bits) - # Cut the bit-stream in 3 parts - # if params.MODULATION_TYPE == 3: + # TODO: refactor and make modular (PAM not handled yet) + if params.MODULATION_TYPE == 3: + # Choose the number of bit streams (depends on the number of frequency ranges) + n_bit_streams = len(params.FREQ_RANGES) - # New structure with bits_per_symbol bits by row - new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] + # Choose the length of our bit streams + len_bit_streams = int(np.ceil(len(new_bits) / (n_bit_streams - 1))) - # Convert this new bits sequence to an integer sequence - ints = [int(b, 2) for b in new_bits] + # Make it even + if len_bit_streams % 2 != 0: + len_bit_streams = len_bit_streams + 1 - if params.logs: - print("Corresponding bytes:\n{}".format(string_bytes)) - print("Cropped and re-structured bits:\n{}".format(new_bits)) - print("Equivalent integers (indices for our mapping):\n{}".format(ints)) - print("--------------------------------------------------------") + # Construct the bit streams array with zeros + bit_streams = np.zeros((n_bit_streams, len_bit_streams), dtype=int) - return ints + # Fill the bit streams arrays + for i in range(len(new_bits)): + bit_streams[i % (n_bit_streams - 1)][int(np.ceil(i / (n_bit_streams - 1)))] = new_bits[i] + # Construct the parity check bit stream and insert it in the bit streams array + pc_bit_stream = np.sum(bit_streams[:n_bit_streams - 1], axis=0) + for i in range(len_bit_streams): + pc_bit_stream[i] = 0 if pc_bit_stream[i] % 2 == 0 else 1 + bit_streams[n_bit_streams - 1] = pc_bit_stream -# TODO: merge both into an encoder method -def encoder(indices, mapping): - """ - :param indices: the mapping indices corresponding to our message - :param mapping: the mapping corresponding to the given modulation type - :return: the symbols/n-tuples - """ - corresponding_symbols = [mapping[i] for i in indices] + if params.logs: + print(" ") + print("Bit stream {}\n: {}".format(bit_streams.shape, bit_streams)) + print("--------------------------------------------------------") + + # Group them by groups of BITS_PER_SYMBOL bits + ints = np.zeros((n_bit_streams, int(len_bit_streams / 2)), dtype=str) + for i in range(n_bit_streams): + for j in range(int(len_bit_streams / params.BITS_PER_SYMBOL)): + grouped_bits = str(bit_streams[i][j]) + str(bit_streams[i][j + params.BITS_PER_SYMBOL - 1]) + mapping_index = int(grouped_bits, base=2) + ints[i][j] = mapping_index + + if params.logs: + print("Ints bits stream {}\n: {}".format(ints.shape, ints)) + print("--------------------------------------------------------") + + elif params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + # New structure with bits_per_symbol bits by row + new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] + + # Convert this new bits sequence to an integer sequence + ints = [[int(b, 2) for b in new_bits]] + + if params.logs: + print("Cropped and re-structured bits:\n{}".format(new_bits)) + print("Equivalent integers (indices for our mapping):\n{}".format(ints)) + print("--------------------------------------------------------") + + else: + raise ValueError("This modulation type does not exist yet... He he he") + + if params.MAPPING == "qam" or params.MAPPING == "psk": + corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) + elif params.MAPPING == "pam": + corresponding_symbols = np.zeros(np.shape(ints), dtype=int) + else: + raise ValueError("This mapping type does not exist yet... He he he") + + for i in range(len(ints)): + print(np.shape(ints)) + corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] if params.logs: print("Mapping the integers to the symbols in the mapping...") print("Symbols/n-tuples to be sent:\n{}".format(corresponding_symbols)) - print("Number of symbols: {}".format(len(corresponding_symbols))) + print("Shape of the symbols: {}".format(np.shape(corresponding_symbols))) print("--------------------------------------------------------") if params.plots: plot_helper.plot_complex_symbols(corresponding_symbols, "{} data symbols to send" - .format(len(corresponding_symbols)), "blue") + .format(np.shape(corresponding_symbols)), "blue") return np.asarray(corresponding_symbols) @@ -94,26 +138,41 @@ def waveform_former(h, data_symbols, USF=params.USF): if params.plots: plot_helper.plot_complex_symbols(preamble_symbols, "Preamble symbols") - # Concatenate the synchronization sequence with the symbols to send - total_symbols = np.concatenate((preamble_symbols, data_symbols, preamble_symbols[::-1])) - if params.plots: - plot_helper.plot_complex_symbols(total_symbols, "Total symbols to send") + # Concatenate the data symbols with the preamble symbols at the beginning and at the end + total_symbols = np.zeros((data_symbols.shape[0], data_symbols.shape[1] + 2 * len(preamble_symbols)), dtype=complex) + for i in range(len(total_symbols)): + total_symbols[i] = np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1])) + + print("Total symbols: {}".format(total_symbols)) + print("Shape of the total symbols: {}".format(np.shape(total_symbols))) - # TODO can/should I remove the ramp-up and ramp_down? (then less samples to send) - # Shape the signal with the pulse h - total_samples = upfirdn(h, total_symbols, USF) + # Shape each of the symbols array + samples = [] + for i in range(len(total_symbols)): + samples.append(upfirdn(h, total_symbols[i], USF)) + + # # Remove the ramp-up and ramp-down of the samples + # cropped_samples = [] + # for i in range(len(samples)): + # # TODO: why + and - 3? Might be wrong + # cropped_samples.append(samples[i][int(params.SPAN/2) + 3:len(samples[i]) - int(params.SPAN/2) - 3]) + # samples = cropped_samples if params.logs: print("Shaping the preamble and the data...") + print("Samples: {}".format(samples)) print("Up-sampling factor: {}".format(params.USF)) - print("Number of samples: {}".format(len(total_samples))) + print("Shape of the total samples: {}".format(np.shape(samples))) print("--------------------------------------------------------") if params.plots: - plot_helper.plot_complex_function(total_samples, "Input samples in Time domain") - plot_helper.fft_plot(total_samples, "Input samples in Frequency domain", shift=True) + for i in range(len(samples)): + plot_helper.plot_complex_function(samples[i], "Input samples {} in Time domain".format(i)) + plot_helper.fft_plot(samples[i], "Input samples {} in Frequency domain".format(i), shift=True) - # Write the preamble samples (base-band, so might be complex) in the preamble_samples file + # Write the preamble samples (base-band, so might be complex) cropped in the preamble_samples file preamble_samples = upfirdn(h, preamble_symbols, USF) + # # TODO: why +3 and - 2? Might be wrong + # preamble_samples = preamble_samples[int(params.SPAN/2) + 3:len(preamble_samples) - int(params.SPAN/2) + 2] read_write.write_preamble_samples(preamble_samples) if params.logs: @@ -124,38 +183,46 @@ def waveform_former(h, data_symbols, USF=params.USF): plot_helper.plot_complex_function(preamble_samples, "Synchronization sequence shaped, in Time domain") plot_helper.fft_plot(preamble_samples, "Synchronization sequence shaped, in Frequency domain", shift=True) + # Choose the modulation frequencies + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 3: + modulating_frequencies = params.np.mean(params.FREQ_RANGES, axis=1) + elif params.MODULATION_TYPE == 2: + modulating_frequencies = [params.FREQ_RANGES[0][1], params.FREQ_RANGES[2][1]] + else: + raise ValueError("This mapping type does not exist yet... He he he") + # Modulate the samples to fit in the required bands - if np.any(np.iscomplex(total_samples)): - if params.MODULATION_TYPE == 1: - total_samples = fourier_helper.modulate_complex_samples(total_samples, - params.np.mean(params.FREQ_RANGES, axis=1)) - elif params.MODULATION_TYPE == 2: - total_samples = fourier_helper.modulate_complex_samples(total_samples, [params.FREQ_RANGES[0][1], - params.FREQ_RANGES[2][1]]) - else: - raise ValueError('This modulation type does not exist yet... Hehehe') + if np.any(np.iscomplex(samples)): + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + samples = fourier_helper.modulate_complex_samples(samples[0], modulating_frequencies) + elif params.MODULATION_TYPE == 3: + modulated_samples = [] + for i in range(len(samples)): + modulated_samples.append(fourier_helper.modulate_complex_samples(samples[i], [modulating_frequencies[i]])) + samples = np.sum(modulated_samples, axis=0).flatten() if params.logs: print("Modulation of the signal...") - print("Minimum sample after modulation: {}".format(min(total_samples))) - print("Maximum sample after modulation: {}".format(max(total_samples))) + print("Minimum sample after modulation: {}".format(min(samples))) + print("Maximum sample after modulation: {}".format(max(samples))) print("--------------------------------------------------------") if params.plots: - plot_helper.plot_complex_function(total_samples, "Input samples after modulation, in Time domain") - plot_helper.fft_plot(total_samples, "Input samples after modulation, in Frequency domain", shift=True) + plot_helper.plot_complex_function(samples, "Input samples after modulation, in Time domain") + plot_helper.fft_plot(samples, "Input samples after modulation, in Frequency domain", shift=True) else: raise ValueError("TODO: handle real samples (e.g SSB)") # Scale the signal to the range [-1, 1] (with a bit of uncertainty margin, according to params.ABS_SAMPLE_RANGE) - total_samples = (total_samples / (max(abs(total_samples))) * params.ABS_SAMPLE_RANGE) + samples = samples / (np.max(np.abs(samples))) * params.ABS_SAMPLE_RANGE if params.logs: print("Scaling the signal...") - print("Minimum sample after scaling: {}".format(min(total_samples))) - print("Maximum sample after scaling: {}".format(max(total_samples))) + print("Number of samples: {}".format(len(samples))) + print("Minimum sample after scaling: {}".format(min(samples))) + print("Maximum sample after scaling: {}".format(max(samples))) print("--------------------------------------------------------") - return total_samples + return samples def send_samples(): @@ -174,11 +241,13 @@ def send_samples(): # Intended for testing (to run the program, run main.py) if __name__ == '__main__': - # Choose the mapping - mapping = mappings.choose_mapping() + moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) + log_file = open("../logs/" + moment + ".log", "w+") + if params.logs: + sys.stdout = log_file # Encode the message - symbols = encoder(message_to_ints(), mapping) + symbols = encoder(mappings.choose_mapping()) # Generate the root-raised_cosine _, h_pulse = pulses.root_raised_cosine() @@ -190,5 +259,5 @@ def send_samples(): read_write.write_samples(input_samples) # Send the samples to the server - send_samples() + # send_samples() From 7a490caabe96d2c8a391093547e79410b30b8e33 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 00:39:17 +0200 Subject: [PATCH 02/20] Improve plots --- src/plot_helper.py | 42 ++++++++++++++++++++++++++++++++++++++++++ src/transmitter.py | 9 +++------ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/plot_helper.py b/src/plot_helper.py index 28422a6..8bad753 100644 --- a/src/plot_helper.py +++ b/src/plot_helper.py @@ -225,3 +225,45 @@ def simple_and_fft_plots(time_indices, samples, title, shift=False): plt.interactive(False) plt.show() return None + + +def samples_fft_plots(samples, title, shift=False, time=False): + num_plots = 3 if any(np.iscomplex(samples)) else 2 + + fig, axs = plt.subplots(num_plots, 1) + fig.suptitle(title) + x_axis = np.arange(len(samples)) + x_label = "Samples" + + if time: + x_axis = x_axis/params.Fs + x_label = "Time (in seconds)" + + axs[0].plot(x_axis, np.real(samples)) + axs[0].set_xlabel(x_label) + axs[0].set_ylabel("Real") + + if num_plots == 3: + axs[1].plot(x_axis, np.imag(samples)) + axs[1].set_xlabel(x_label) + axs[1].set_ylabel("Imaginary") + + X = np.fft.fft(samples) + f_x, y_x = fourier_helper.dft_map(X, shift=shift) + + axs[num_plots - 1].plot(f_x, abs(y_x)) + axs[num_plots - 1].set_xlabel("Frequency (in Hertz)") + axs[num_plots - 1].set_ylabel("|X(f)|^2") + + for i in range(num_plots): + axs[i].grid(True) + + vertical_lines_frequency_ranges(axs[num_plots - 1]) + + if not shift: + axs[num_plots - 1].set_xlim(params.FREQ_RANGES[0][0] - 1000, params.FREQ_RANGES[3][1] + 1000) + + plt.subplots_adjust(hspace=0.5) + plt.interactive(False) + plt.show() + return None diff --git a/src/transmitter.py b/src/transmitter.py index e4ba9f9..4f6823d 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -166,8 +166,7 @@ def waveform_former(h, data_symbols, USF=params.USF): print("--------------------------------------------------------") if params.plots: for i in range(len(samples)): - plot_helper.plot_complex_function(samples[i], "Input samples {} in Time domain".format(i)) - plot_helper.fft_plot(samples[i], "Input samples {} in Frequency domain".format(i), shift=True) + plot_helper.samples_fft_plots(samples[i], "Samples {}".format(i), shift=True) # Write the preamble samples (base-band, so might be complex) cropped in the preamble_samples file preamble_samples = upfirdn(h, preamble_symbols, USF) @@ -180,8 +179,7 @@ def waveform_former(h, data_symbols, USF=params.USF): print("Number of samples for the preamble: {}".format(len(preamble_samples))) print("--------------------------------------------------------") if params.plots: - plot_helper.plot_complex_function(preamble_samples, "Synchronization sequence shaped, in Time domain") - plot_helper.fft_plot(preamble_samples, "Synchronization sequence shaped, in Frequency domain", shift=True) + plot_helper.samples_fft_plots(preamble_samples, "Preamble samples", shift=True) # Choose the modulation frequencies if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 3: @@ -207,8 +205,7 @@ def waveform_former(h, data_symbols, USF=params.USF): print("Maximum sample after modulation: {}".format(max(samples))) print("--------------------------------------------------------") if params.plots: - plot_helper.plot_complex_function(samples, "Input samples after modulation, in Time domain") - plot_helper.fft_plot(samples, "Input samples after modulation, in Frequency domain", shift=True) + plot_helper.samples_fft_plots(samples, "Samples to send", time=True) else: raise ValueError("TODO: handle real samples (e.g SSB)") From aff906bd1c68788137b4f2fbce45d85fdde58a39 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 01:34:41 +0200 Subject: [PATCH 03/20] Improve plots, continue receiver --- src/plot_helper.py | 19 +++++++++++++++++++ src/receiver.py | 42 ++++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/plot_helper.py b/src/plot_helper.py index 8bad753..c1ef1ce 100644 --- a/src/plot_helper.py +++ b/src/plot_helper.py @@ -267,3 +267,22 @@ def samples_fft_plots(samples, title, shift=False, time=False): plt.interactive(False) plt.show() return None + + +def delay_plots(samples, delay, title): + fig, axs = plt.subplots(3, 1) + fig.suptitle(title) + x_axis = np.arange(len(samples[0])) / params.Fs + x_label = "Time (in seconds)" + + for i in range(len(samples)): + axs[i].plot(x_axis, np.real(samples[i])) + axs[i].set_ylabel("Samples {}".format(i)) + axs[i].axvline(x=delay / params.Fs, color='black') + axs[i].grid(True) + axs[len(samples) - 1].set_xlabel(x_label) + + plt.subplots_adjust(hspace=0.5) + plt.interactive(False) + plt.show() + return None diff --git a/src/receiver.py b/src/receiver.py index c2a3c9a..2fac347 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -100,18 +100,12 @@ def received_from_server(): # Plot the input and output samples in Time domain and Frequency domain if params.plots: - plot_helper.two_simple_plots(input_samples, received_samples, "Input and output in Time domain", "Input", - "Output") - plot_helper.two_fft_plots(input_samples, received_samples, "Input and output in Frequency domain", "Input", - "Output") + plot_helper.samples_fft_plots(input_samples, "Sent samples") + plot_helper.samples_fft_plots(received_samples, "Received samples") # Read the preamble samples saved previously preamble_samples = read_write.read_preamble_samples() len_preamble_samples = len(preamble_samples) - - if params.plots: - plot_helper.plot_complex_function(received_samples, "Received samples in time domain") - plot_helper.fft_plot(received_samples, "Received samples in frequency domain", shift=True) if params.logs: print("--------------------------------------------------------") # ------------------------------------------------------------------------------------------------------------------ @@ -125,6 +119,10 @@ def received_from_server(): # Array of the form [True, True, False, True], where False means that the 3rd frequency range is removes here frequency_ranges_available = [True if i != removed_freq_range else False for i in range(len(params.FREQ_RANGES))] + indices_available = [] + for i in range(len(frequency_ranges_available)): + if frequency_ranges_available[i]: + indices_available.append(i) if params.logs: print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) print("--------------------------------------------------------") @@ -148,8 +146,7 @@ def received_from_server(): demodulated_samples = fourier_helper.demodulate(received_samples, fc) if params.plots: - plot_helper.plot_complex_function(demodulated_samples, "Demodulated samples in Time domain") - plot_helper.fft_plot(demodulated_samples, "Demodulated samples in Frequency domain", shift=True) + plot_helper.samples_fft_plots(demodulated_samples, "Demodulated received samples") elif params.MODULATION_TYPE == 3: demodulated_samples = [] @@ -158,6 +155,11 @@ def received_from_server(): demodulated_samples.append(fourier_helper.demodulate(received_samples, np.mean(params.FREQ_RANGES[i]))) else: demodulated_samples.append([]) + for i in range(len(indices_available)): + plot_helper.samples_fft_plots( + demodulated_samples[indices_available[i]], + "Demodulated received samples {}".format(indices_available[i]), shift=True) + else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -174,15 +176,15 @@ def received_from_server(): if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: y = np.convolve(demodulated_samples, h_matched) if params.plots: - plot_helper.plot_complex_function(y, "y in Time domain") - plot_helper.fft_plot(y, "y in Frequency domain", shift=True) + plot_helper.samples_fft_plots(y, "Low-passed samples", shift=True) elif params.MODULATION_TYPE == 3: y = [] for i in range(len(demodulated_samples)): if frequency_ranges_available[i]: y.append(np.convolve(demodulated_samples[i], h_matched)) - else: - y.append([]) + for i in range(len(indices_available)): + plot_helper.samples_fft_plots( + y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -203,6 +205,8 @@ def received_from_server(): if frequency_ranges_available[i]: delays.append(parameter_estim.ML_theta_estimation(y[i], preamble_samples)) delay = int(np.round(np.mean(delays))) + if params.plots: + plot_helper.delay_plots(y, delay, "Delays estimated (only real part is shown)") else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -259,12 +263,10 @@ def received_from_server(): if frequency_ranges_available[i]: data_samples.append(y[i][delay + len_preamble_samples - half_span_h + params.USF:]) if params.plots: - j = 0 - for i in range(len(y)): - if frequency_ranges_available[i]: - plot_helper.plot_complex_function( - data_samples[j], "y[{}] after removing the delay, the preamble, and adjusting".format(i)) - j += 1 + for i in range(len(indices_available)): + plot_helper.plot_complex_function(data_samples[indices_available[i]], + "y[{}] after removing the delay, the preamble, and adjusting". + format(indices_available[i])) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: From ab0927ea44713a676e7664df1ba052dee8596ce6 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 17:01:19 +0200 Subject: [PATCH 04/20] Local test maps an untouched transmitted signal to the right symbols... FINALLY --- src/local_test.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/local_test.py b/src/local_test.py index 5a3f9d6..d64795e 100644 --- a/src/local_test.py +++ b/src/local_test.py @@ -143,9 +143,7 @@ def local_test(): Test the design locally with modulation and demodulation :return: None """ - mapping = mappings.choose_mapping() - ints = transmitter.message_to_ints() - symbols = transmitter.encoder(ints, mapping) + symbols = transmitter.encoder(mappings.choose_mapping()) # Generate the pulse h _, h = pulses.root_raised_cosine() @@ -160,7 +158,7 @@ def local_test(): len_preamble_samples = len(preamble_samples) # Concatenate the preamble symbols with the data symbols - total_symbols = np.concatenate((preamble_symbols, symbols, preamble_symbols[::-1])) + total_symbols = np.concatenate((preamble_symbols, symbols[0], preamble_symbols[::-1])) # Shape the signal with the pulse h total_samples = upfirdn(h, total_symbols, params.USF) @@ -208,7 +206,7 @@ def local_test(): # ---------------------------------------------------------------------------------------------------------------- # Channel simulation --------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- - samples = server_simulation(samples, filter_freq=False) + # samples = server_simulation(samples, filter_freq=False) # ---------------------------------------------------------------------------------------------------------------- # Channel simulation's end --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- @@ -246,12 +244,12 @@ def local_test(): plot_helper.fft_plot(y, "y in Frequency domain", shift=True) # Find the delay - delay = parameter_estim.ML_theta_estimation(demodulated_samples, preamble_samples=preamble_samples) + delay = parameter_estim.ML_theta_estimation(y, preamble_samples=preamble_samples) print("Delay: {} samples".format(delay)) print("--------------------------------------------------------") # Extract the preamble samples - preamble_samples_received = y[half_span_h + delay - 1:half_span_h + delay + len_preamble_samples - 1] + preamble_samples_received = y[delay:delay + len_preamble_samples] plot_helper.two_simple_plots(preamble_samples_received.real, preamble_samples.real, "Comparison between preamble samples received and preamble samples sent", "received", "expected") @@ -277,7 +275,7 @@ def local_test(): print("Scaling factor: {}".format(scaling_factor)) # Crop the samples (remove the delay, the preamble, and the ramp-up) - data_samples = y[half_span_h + delay + len_preamble_samples - half_span_h + params.USF - 1 - 1:] + data_samples = y[delay + len_preamble_samples - half_span_h + params.USF:] # Find the second_preamble_index second_preamble_index = parameter_estim.ML_theta_estimation(data_samples, preamble_samples=preamble_samples[::-1]) @@ -299,7 +297,7 @@ def local_test(): plot_helper.plot_complex_symbols(data_symbols, "Symbols received", annotate=False) # Decode the symbols - ints = receiver.decoder(data_symbols, mapping) + ints = receiver.decoder(data_symbols, mappings.choose_mapping()) message_received = receiver.ints_to_message(ints) message_file = open(params.input_message_file_path) @@ -309,11 +307,4 @@ def local_test(): # Intended for testing (to run the program, run main.py) if __name__ == "__main__": - _, h = pulses.root_raised_cosine() - a = [1, 1] - samples = upfirdn(h, a, params.USF) - print(samples) - print(np.argmax(samples)) - print(len(samples)) - plot_helper.plot_complex_function(samples, "Test rrc") - + local_test() From 0ed9b833c0a14c6cd4c901a020cc6efbe0e5e1a3 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 17:18:03 +0200 Subject: [PATCH 05/20] New transmitter and receiver work in local --- src/local_test.py | 9 ++-- src/main.py | 4 +- src/params.py | 8 +-- src/plot_helper.py | 16 ++++++ src/receiver.py | 125 +++++++++++++++++++++++++-------------------- src/transmitter.py | 44 ++++++---------- 6 files changed, 115 insertions(+), 91 deletions(-) diff --git a/src/local_test.py b/src/local_test.py index d64795e..f9b86db 100644 --- a/src/local_test.py +++ b/src/local_test.py @@ -143,14 +143,14 @@ def local_test(): Test the design locally with modulation and demodulation :return: None """ - symbols = transmitter.encoder(mappings.choose_mapping()) + data_symbols = transmitter.encoder(mappings.choose_mapping()) # Generate the pulse h _, h = pulses.root_raised_cosine() half_span_h = int(params.SPAN / 2) # Generate the preamble symbols and read it from the corresponding file - preambles.generate_preamble_symbols(len(symbols)) + preambles.generate_preamble_symbols(len(data_symbols)) preamble_symbols = read_write.read_preamble_symbols() # Generate the preamble samples @@ -158,7 +158,7 @@ def local_test(): len_preamble_samples = len(preamble_samples) # Concatenate the preamble symbols with the data symbols - total_symbols = np.concatenate((preamble_symbols, symbols[0], preamble_symbols[::-1])) + total_symbols = np.concatenate((preamble_symbols, data_symbols[0], preamble_symbols[::-1])) # Shape the signal with the pulse h total_samples = upfirdn(h, total_symbols, params.USF) @@ -203,10 +203,13 @@ def local_test(): print("Maximum sample after scaling: {}".format(max(samples))) print("--------------------------------------------------------") + # read_write.write_samples(samples) + # ---------------------------------------------------------------------------------------------------------------- # Channel simulation --------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- # samples = server_simulation(samples, filter_freq=False) + samples = np.loadtxt(params.input_sample_file_path) # ---------------------------------------------------------------------------------------------------------------- # Channel simulation's end --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- diff --git a/src/main.py b/src/main.py index 7d58346..508f6b3 100644 --- a/src/main.py +++ b/src/main.py @@ -9,9 +9,9 @@ import transmitter if __name__ == "__main__": - moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) - log_file = open("../logs/" + moment + ".log", "w+") if params.logs: + moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) + log_file = open("../logs/" + moment + ".log", "w+") sys.stdout = log_file # Write the parameters in the log file diff --git a/src/params.py b/src/params.py index 185a48c..2ede442 100644 --- a/src/params.py +++ b/src/params.py @@ -45,20 +45,20 @@ def choose_symbol_period(): M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MODULATION_TYPE = 3 +MODULATION_TYPE = 1 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) # 3 = parity check approach (only works for M=4 here) -MODULATION_TYPE_1_BANDWIDTH = 2000 +MODULATION_TYPE_1_BANDWIDTH = 1900 MODULATION_TYPE_2_BANDWIDTH = 4000 -MODULATION_TYPE_3_BANDWIDTH = 2000 +MODULATION_TYPE_3_BANDWIDTH = 1900 BETA = 0.2 # rolloff factor of our root-raised-cosine pulse (usually between 0.2 and 0.3 (said Prandoni)) T = choose_symbol_period() # symbol period (in seconds), i.e time before we can repeat the pulse while satisfying # Nyquist crit. NORMALIZE_PULSE = True # rather we normalize the pulse or not -USF = int(np.ceil(T * Fs)) # up-sampling factor, i.e the number of zeros to add between any 2 symbols before +USF = int(T * Fs) # up-sampling factor, i.e the number of zeros to add between any 2 symbols before # pulse shaping SPAN = 20 * USF # size of our pulse in number of samples diff --git a/src/plot_helper.py b/src/plot_helper.py index c1ef1ce..4012a92 100644 --- a/src/plot_helper.py +++ b/src/plot_helper.py @@ -286,3 +286,19 @@ def delay_plots(samples, delay, title): plt.interactive(False) plt.show() return None + + +def compare_preambles(preamble_received, preamble_sent, title): + fig, axs = plt.subplots(2, 2) + fig.suptitle(title) + x_axis = np.arange(max(len(preamble_sent), len(preamble_received))) + + axs[0][0].plot(x_axis, np.real(preamble_sent)) + axs[0][0].set_ylabel("Real") + axs[1][0].plot(x_axis, np.imag(preamble_sent)) + axs[1][0].set_ylabel("Imaginary") + + axs[0][1].plot(x_axis, np.real(preamble_received)) + axs[0][1].set_ylabel("Real") + axs[1][1].plot(x_axis, np.imag(preamble_received)) + axs[1][1].set_ylabel("Imaginary") diff --git a/src/receiver.py b/src/receiver.py index 2fac347..74445ba 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -1,4 +1,6 @@ import numpy as np +import time +import sys import fourier_helper import helper @@ -18,28 +20,10 @@ def decoder(y, mapping): :param mapping: the chosen mapping for the communication :return: integers between 0 and M-1, i.e integers corresponding to the bits sent """ - - # Make sure y and mapping have less or equal than 2 dimensions - if len(y.shape) > 2 or len(mapping.shape) > 2: - raise AttributeError("One of the vectors y and mapping has more than 2 dimensions!") - - # If y is a column vector, make it a row vector - n_elems_axis_0_y = np.size(y, 0) - if n_elems_axis_0_y != 1: - y = y.reshape(1, n_elems_axis_0_y) - else: - y = y.reshape(1, np.size(y, 1)) - - # If mapping is a row vector, make it a column vector - if np.size(mapping, 0) == 1: - mapping = mapping.reshape(np.size(mapping, 1), 1) - else: - mapping = mapping.reshape(np.size(mapping, 0), 1) - # Number of symbols in the mapping - M = np.size(mapping, 0) + M = len(mapping) # Number of symbols received - S = np.size(y, 1) + S = len(y) distances = np.transpose(abs(np.tile(y, (M, 1)) - np.tile(mapping, (1, S)))) ints = np.argmin(distances, 1) @@ -125,6 +109,7 @@ def received_from_server(): indices_available.append(i) if params.logs: print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) + print("Available indices array: {}".format(indices_available)) print("--------------------------------------------------------") # ------------------------------------------------------------------------------------------------------------------ @@ -146,18 +131,20 @@ def received_from_server(): demodulated_samples = fourier_helper.demodulate(received_samples, fc) if params.plots: - plot_helper.samples_fft_plots(demodulated_samples, "Demodulated received samples") + plot_helper.samples_fft_plots(demodulated_samples, "Demodulated received samples", shift=True) elif params.MODULATION_TYPE == 3: demodulated_samples = [] + demodulation_frequencies = np.mean(params.FREQ_RANGES, axis=1) for i in range(len(params.FREQ_RANGES)): if frequency_ranges_available[i]: - demodulated_samples.append(fourier_helper.demodulate(received_samples, np.mean(params.FREQ_RANGES[i]))) - else: - demodulated_samples.append([]) - for i in range(len(indices_available)): + demodulated_samples.append(fourier_helper.demodulate(received_samples, demodulation_frequencies[i])) + if params.logs: + print("Demodulation frequencies: {}".format(demodulation_frequencies)) + if params.plots: + for i in range(len(indices_available)): plot_helper.samples_fft_plots( - demodulated_samples[indices_available[i]], + demodulated_samples[i], "Demodulated received samples {}".format(indices_available[i]), shift=True) else: @@ -177,20 +164,22 @@ def received_from_server(): y = np.convolve(demodulated_samples, h_matched) if params.plots: plot_helper.samples_fft_plots(y, "Low-passed samples", shift=True) + if params.logs: + print("Length of y: {}".format(len(y))) elif params.MODULATION_TYPE == 3: y = [] for i in range(len(demodulated_samples)): - if frequency_ranges_available[i]: - y.append(np.convolve(demodulated_samples[i], h_matched)) - for i in range(len(indices_available)): + y.append(np.convolve(demodulated_samples[i], h_matched)) + for i in range(len(y)): plot_helper.samples_fft_plots( y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) + if params.logs: + print("Y shape:") + for i in range(len(y)): + print(np.shape(y[i])) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: - print("Y shape:") - for i in range(len(y)): - print(np.shape(y[i])) print("--------------------------------------------------------") # ------------------------------------------------------------------------------------------------------------------ @@ -219,20 +208,26 @@ def received_from_server(): print("Extracting the preamble samples...") if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: preamble_samples_received = y[delay:delay + len_preamble_samples] + if params.plots: + plot_helper.two_simple_plots(np.real(preamble_samples_received), np.real(preamble_samples), + "Preamble samples received vs preamble samples sent", "received", "expected") + if params.logs: + print("Number of samples for the actual preamble: {} samples".format(len_preamble_samples)) + print("Number of samples for the received preamble: {} samples".format(len(preamble_samples_received))) elif params.MODULATION_TYPE == 3: preamble_samples_received = [] for i in range(len(y)): - if frequency_ranges_available[i]: - preamble_samples_received.append(y[i][delay:delay + len_preamble_samples]) - preamble_samples_received = np.mean(preamble_samples_received, axis=0) + preamble_samples_received.append(y[i][delay:delay + len_preamble_samples]) + if params.plots: + for i in range(len(preamble_samples_received)): + if frequency_ranges_available[i]: + plot_helper.compare_preambles(preamble_samples_received[i], preamble_samples, + "Preamble samples received {} vs preamble samples sent" + .format(indices_available[i])) else: raise ValueError('This modulation type does not exist yet... He he he') - if params.plots: - plot_helper.two_simple_plots(np.real(preamble_samples_received), np.real(preamble_samples), - "Preamble samples received vs preamble samples sent", "received", "expected") + if params.logs: - # print("Number of samples for the actual preamble: {} samples".format(len_preamble_samples)) - # print("Number of samples for the received preamble: {} samples".format(len(preamble_samples_received))) print("--------------------------------------------------------") # ------------------------------------------------------------------------------------------------------------------ @@ -240,12 +235,29 @@ def received_from_server(): if params.logs: print("Computing the phase shift and the scaling factor...") # Remove SPAN/2 samples in the end because there is still data there for the received preamble - phase_shift_estim, scaling_factor = parameter_estim.ML_phase_scaling_estim( - preamble_samples[:len_preamble_samples - half_span_h], - preamble_samples_received[:len(preamble_samples_received) - half_span_h]) + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + phase_shift_estim, scaling_factor_estim = parameter_estim.ML_phase_scaling_estim( + preamble_samples[:len_preamble_samples - half_span_h], + preamble_samples_received[:len(preamble_samples_received) - half_span_h]) + if params.logs: + print("Phase shift: {}".format(phase_shift_estim)) + print("Scaling factor: {}".format(scaling_factor_estim)) + elif params.MODULATION_TYPE == 3: + phase_shift_estim_array = [] + scaling_factor_estim_array = [] + for i in range(len(preamble_samples_received)): + phase_shift_estim_in_range, scaling_factor_estim_in_range = parameter_estim.ML_phase_scaling_estim( + preamble_samples[:len_preamble_samples - half_span_h], + preamble_samples_received[i][:len(preamble_samples_received[i]) - half_span_h]) + phase_shift_estim_array.append(phase_shift_estim_in_range) + scaling_factor_estim_array.append(scaling_factor_estim_in_range) + if params.logs: + for i in range(len(phase_shift_estim_array)): + print("Phase shift {}: {}".format(indices_available[i], phase_shift_estim_array[i])) + print("Scaling factor {}: {}".format(indices_available[i], scaling_factor_estim_array[i])) + else: + raise ValueError('This modulation type does not exist yet... He he he') if params.logs: - print("Phase shift: {}".format(phase_shift_estim)) - print("Scaling factor: {}".format(scaling_factor)) print("--------------------------------------------------------") # ------------------------------------------------------------------------------------------------------------------ @@ -254,22 +266,23 @@ def received_from_server(): print("Cropping the samples (removing the delay, the preamble, " "and adjusting to the first relevant sample of data)...") if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - data_samples = y[delay + len_preamble_samples - half_span_h + params.USF - 1:] + data_samples = y[delay + len_preamble_samples - half_span_h + params.USF:] if params.plots: + plot_helper.plot_complex_function(y, "y before removing anything") plot_helper.plot_complex_function(data_samples, "y after removing the delay, the preamble, and adjusting") elif params.MODULATION_TYPE == 3: data_samples = [] for i in range(len(y)): - if frequency_ranges_available[i]: - data_samples.append(y[i][delay + len_preamble_samples - half_span_h + params.USF:]) + data_samples.append(y[i][delay + len_preamble_samples - 1 - half_span_h + 1 + params.USF:]) if params.plots: - for i in range(len(indices_available)): - plot_helper.plot_complex_function(data_samples[indices_available[i]], + for i in range(len(data_samples)): + plot_helper.plot_complex_function(data_samples[i], "y[{}] after removing the delay, the preamble, and adjusting". format(indices_available[i])) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: + print(params.USF) print("--------------------------------------------------------") # ------------------------------------------------------------------------------------------------------------------ @@ -281,12 +294,9 @@ def received_from_server(): preamble_samples=preamble_samples[::-1]) elif params.MODULATION_TYPE == 3: second_preamble_index = [] - j = 0 for i in range(len(data_samples)): - if frequency_ranges_available[i]: second_preamble_index.append(parameter_estim.ML_theta_estimation( - data_samples[j], preamble_samples=preamble_samples[::-1])) - j += 1 + data_samples[i], preamble_samples=preamble_samples[::-1])) second_preamble_index = int(np.round(np.mean(second_preamble_index))) else: raise ValueError('This modulation type does not exist yet... He he he') @@ -320,8 +330,9 @@ def received_from_server(): if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: data_samples = data_samples * np.exp(-1j * (phase_shift_estim - np.pi / 2)) elif params.MODULATION_TYPE == 3: + print(len(data_samples)) for i in range(len(data_samples)): - data_samples[i] = data_samples[i] * np.exp(-1j * (phase_shift_estim - np.pi / 2)) + data_samples[i] = data_samples[i] * np.exp(-1j * (phase_shift_estim_array[i] - np.pi / 2)) else: raise ValueError("This modulation type does not exist yet... He he he") if params.logs: @@ -371,6 +382,10 @@ def received_from_server(): # Intended for testing (to run the program, run main.py) if __name__ == "__main__": + if params.logs: + moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) + log_file = open("../logs/" + moment + ".log", "w+") + sys.stdout = log_file received_from_server() # TODO: make sure the scaling factor, delay, phase shift must be the same for 3 freq ranges diff --git a/src/transmitter.py b/src/transmitter.py index 4f6823d..09f621e 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -42,7 +42,18 @@ def encoder(mapping): new_bits = ''.join(new_bits) # TODO: refactor and make modular (PAM not handled yet) - if params.MODULATION_TYPE == 3: + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + # New structure with bits_per_symbol bits by row + new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] + + # Convert this new bits sequence to an integer sequence + ints = [[int(b, 2) for b in new_bits]] + + if params.logs: + print("Cropped and re-structured bits:\n{}".format(new_bits)) + print("Equivalent integers (indices for our mapping):\n{}".format(ints)) + print("--------------------------------------------------------") + elif params.MODULATION_TYPE == 3: # Choose the number of bit streams (depends on the number of frequency ranges) n_bit_streams = len(params.FREQ_RANGES) @@ -82,29 +93,10 @@ def encoder(mapping): if params.logs: print("Ints bits stream {}\n: {}".format(ints.shape, ints)) print("--------------------------------------------------------") - - elif params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - # New structure with bits_per_symbol bits by row - new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] - - # Convert this new bits sequence to an integer sequence - ints = [[int(b, 2) for b in new_bits]] - - if params.logs: - print("Cropped and re-structured bits:\n{}".format(new_bits)) - print("Equivalent integers (indices for our mapping):\n{}".format(ints)) - print("--------------------------------------------------------") - else: raise ValueError("This modulation type does not exist yet... He he he") - if params.MAPPING == "qam" or params.MAPPING == "psk": - corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) - elif params.MAPPING == "pam": - corresponding_symbols = np.zeros(np.shape(ints), dtype=int) - else: - raise ValueError("This mapping type does not exist yet... He he he") - + corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) for i in range(len(ints)): print(np.shape(ints)) corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] @@ -128,7 +120,6 @@ def waveform_former(h, data_symbols, USF=params.USF): :param USF: the up-sampling factor (number of samples per symbols) :return: the samples of a modulated pulse train to send to the server """ - # Generate the preamble_symbols and write them in the appropriate file preambles.generate_preamble_symbols(len(data_symbols)) preamble_symbols = read_write.read_preamble_symbols() @@ -170,8 +161,6 @@ def waveform_former(h, data_symbols, USF=params.USF): # Write the preamble samples (base-band, so might be complex) cropped in the preamble_samples file preamble_samples = upfirdn(h, preamble_symbols, USF) - # # TODO: why +3 and - 2? Might be wrong - # preamble_samples = preamble_samples[int(params.SPAN/2) + 3:len(preamble_samples) - int(params.SPAN/2) + 2] read_write.write_preamble_samples(preamble_samples) if params.logs: @@ -238,10 +227,11 @@ def send_samples(): # Intended for testing (to run the program, run main.py) if __name__ == '__main__': - moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) - log_file = open("../logs/" + moment + ".log", "w+") if params.logs: + moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) + log_file = open("../logs/" + moment + ".log", "w+") sys.stdout = log_file + params.params_log() # Encode the message symbols = encoder(mappings.choose_mapping()) @@ -256,5 +246,5 @@ def send_samples(): read_write.write_samples(input_samples) # Send the samples to the server - # send_samples() + send_samples() From a52bfb483f0ab4d01f25faac69952d346d3e0829 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 17:53:45 +0200 Subject: [PATCH 06/20] Refactor the transmitter a bit --- src/params.py | 2 +- src/plot_helper.py | 4 +-- src/receiver.py | 6 ++-- src/transmitter.py | 71 +++++++++++++++++++-------------------- src/transmitter_helper.py | 0 5 files changed, 41 insertions(+), 42 deletions(-) create mode 100644 src/transmitter_helper.py diff --git a/src/params.py b/src/params.py index 2ede442..4e545c1 100644 --- a/src/params.py +++ b/src/params.py @@ -45,7 +45,7 @@ def choose_symbol_period(): M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MODULATION_TYPE = 1 +MODULATION_TYPE = 3 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) # 3 = parity check approach (only works for M=4 here) diff --git a/src/plot_helper.py b/src/plot_helper.py index 4012a92..be7b24e 100644 --- a/src/plot_helper.py +++ b/src/plot_helper.py @@ -227,8 +227,8 @@ def simple_and_fft_plots(time_indices, samples, title, shift=False): return None -def samples_fft_plots(samples, title, shift=False, time=False): - num_plots = 3 if any(np.iscomplex(samples)) else 2 +def samples_fft_plots(samples, title, shift=False, time=False, complex=True): + num_plots = 2 if not complex else 3 fig, axs = plt.subplots(num_plots, 1) fig.suptitle(title) diff --git a/src/receiver.py b/src/receiver.py index 74445ba..26c5705 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -80,12 +80,12 @@ def received_from_server(): # Load the input and output samples from their respective files input_samples = np.loadtxt(params.input_sample_file_path) # TODO: put output again - received_samples = np.loadtxt(params.input_sample_file_path) + received_samples = np.loadtxt(params.output_sample_file_path) # Plot the input and output samples in Time domain and Frequency domain if params.plots: - plot_helper.samples_fft_plots(input_samples, "Sent samples") - plot_helper.samples_fft_plots(received_samples, "Received samples") + plot_helper.samples_fft_plots(input_samples, "Sent samples", complex=False) + plot_helper.samples_fft_plots(received_samples, "Received samples", complex=False) # Read the preamble samples saved previously preamble_samples = read_write.read_preamble_samples() diff --git a/src/transmitter.py b/src/transmitter.py index 09f621e..11174d1 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -96,6 +96,7 @@ def encoder(mapping): else: raise ValueError("This modulation type does not exist yet... He he he") + # TODO: make it work for PAM corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) for i in range(len(ints)): print(np.shape(ints)) @@ -130,36 +131,28 @@ def waveform_former(h, data_symbols, USF=params.USF): plot_helper.plot_complex_symbols(preamble_symbols, "Preamble symbols") # Concatenate the data symbols with the preamble symbols at the beginning and at the end - total_symbols = np.zeros((data_symbols.shape[0], data_symbols.shape[1] + 2 * len(preamble_symbols)), dtype=complex) - for i in range(len(total_symbols)): - total_symbols[i] = np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1])) - - print("Total symbols: {}".format(total_symbols)) - print("Shape of the total symbols: {}".format(np.shape(total_symbols))) + p_data_p_symbols = [] + for i in range(len(data_symbols)): + p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) + print("Total symbols: {}".format(p_data_p_symbols)) + print("Shape of total symbols: {}".format(np.shape(p_data_p_symbols))) # Shape each of the symbols array - samples = [] - for i in range(len(total_symbols)): - samples.append(upfirdn(h, total_symbols[i], USF)) - - # # Remove the ramp-up and ramp-down of the samples - # cropped_samples = [] - # for i in range(len(samples)): - # # TODO: why + and - 3? Might be wrong - # cropped_samples.append(samples[i][int(params.SPAN/2) + 3:len(samples[i]) - int(params.SPAN/2) - 3]) - # samples = cropped_samples + p_data_p_samples = [] + for i in range(len(p_data_p_symbols)): + p_data_p_samples.append(upfirdn(h, p_data_p_symbols[i], USF)) if params.logs: print("Shaping the preamble and the data...") - print("Samples: {}".format(samples)) + print("Samples: {}".format(p_data_p_samples)) print("Up-sampling factor: {}".format(params.USF)) - print("Shape of the total samples: {}".format(np.shape(samples))) + print("Shape of the total samples: {}".format(np.shape(p_data_p_samples))) print("--------------------------------------------------------") if params.plots: - for i in range(len(samples)): - plot_helper.samples_fft_plots(samples[i], "Samples {}".format(i), shift=True) + for i in range(len(p_data_p_samples)): + plot_helper.samples_fft_plots(p_data_p_samples[i], "Samples {}".format(i), shift=True) - # Write the preamble samples (base-band, so might be complex) cropped in the preamble_samples file + # Write the preamble samples in the preamble_samples file preamble_samples = upfirdn(h, preamble_symbols, USF) read_write.write_preamble_samples(preamble_samples) @@ -179,36 +172,42 @@ def waveform_former(h, data_symbols, USF=params.USF): raise ValueError("This mapping type does not exist yet... He he he") # Modulate the samples to fit in the required bands - if np.any(np.iscomplex(samples)): + if np.any(np.iscomplex(p_data_p_samples)): if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - samples = fourier_helper.modulate_complex_samples(samples[0], modulating_frequencies) + p_data_p_samples = [item for sublist in p_data_p_samples for item in sublist] + p_data_p_modulated_samples = fourier_helper.modulate_complex_samples(p_data_p_samples, + modulating_frequencies) elif params.MODULATION_TYPE == 3: modulated_samples = [] - for i in range(len(samples)): - modulated_samples.append(fourier_helper.modulate_complex_samples(samples[i], [modulating_frequencies[i]])) - samples = np.sum(modulated_samples, axis=0).flatten() + for i in range(len(p_data_p_samples)): + modulated_samples.append(fourier_helper.modulate_complex_samples(p_data_p_samples[i], + [modulating_frequencies[i]])) + p_data_p_modulated_samples = np.sum(modulated_samples, axis=0).flatten() + else: + raise ValueError("This mapping type does not exist yet... He he he") if params.logs: print("Modulation of the signal...") - print("Minimum sample after modulation: {}".format(min(samples))) - print("Maximum sample after modulation: {}".format(max(samples))) + print("Minimum sample after modulation: {}".format(min(p_data_p_samples))) + print("Maximum sample after modulation: {}".format(max(p_data_p_samples))) print("--------------------------------------------------------") if params.plots: - plot_helper.samples_fft_plots(samples, "Samples to send", time=True) + plot_helper.samples_fft_plots(p_data_p_samples, "Samples to send", time=True, complex=True, shift=True) else: raise ValueError("TODO: handle real samples (e.g SSB)") # Scale the signal to the range [-1, 1] (with a bit of uncertainty margin, according to params.ABS_SAMPLE_RANGE) - samples = samples / (np.max(np.abs(samples))) * params.ABS_SAMPLE_RANGE + samples_to_send = p_data_p_modulated_samples / (np.max(np.abs(p_data_p_modulated_samples))) * params.\ + ABS_SAMPLE_RANGE if params.logs: print("Scaling the signal...") - print("Number of samples: {}".format(len(samples))) - print("Minimum sample after scaling: {}".format(min(samples))) - print("Maximum sample after scaling: {}".format(max(samples))) + print("Number of samples: {}".format(len(samples_to_send))) + print("Minimum sample after scaling: {}".format(min(samples_to_send))) + print("Maximum sample after scaling: {}".format(max(samples_to_send))) print("--------------------------------------------------------") - return samples + return samples_to_send def send_samples(): @@ -240,10 +239,10 @@ def send_samples(): _, h_pulse = pulses.root_raised_cosine() # Construct the samples to send - input_samples = waveform_former(h_pulse, symbols) + samples = waveform_former(h_pulse, symbols) # Write the samples in the input file - read_write.write_samples(input_samples) + read_write.write_samples(samples) # Send the samples to the server send_samples() diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py new file mode 100644 index 0000000..e69de29 From ecb8f43b659214bcdc801645df833efadfa836e3 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 18:51:32 +0200 Subject: [PATCH 07/20] Refactor waveform former with the transmitter helper --- src/transmitter.py | 103 +++++---------------------- src/transmitter_helper.py | 142 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 84 deletions(-) diff --git a/src/transmitter.py b/src/transmitter.py index 11174d1..af0faab 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -2,16 +2,14 @@ import time import sys import numpy as np -from scipy.signal import upfirdn -import fourier_helper import helper import mappings import params import plot_helper -import preambles import pulses import read_write +import transmitter_helper def encoder(mapping): @@ -117,95 +115,32 @@ def encoder(mapping): def waveform_former(h, data_symbols, USF=params.USF): """ :param h: the sampled pulse - :param data_symbols: the symbols modulating the pulse - :param USF: the up-sampling factor (number of samples per symbols) + :param data_symbols: the data symbols modulating the pulse + :param USF: the up-sampling factor, i.e the number of samples per symbols, also called SPS :return: the samples of a modulated pulse train to send to the server """ - # Generate the preamble_symbols and write them in the appropriate file - preambles.generate_preamble_symbols(len(data_symbols)) - preamble_symbols = read_write.read_preamble_symbols() - if params.logs: - print("Preamble symbols:\n{}".format(preamble_symbols)) - print("--------------------------------------------------------") - if params.plots: - plot_helper.plot_complex_symbols(preamble_symbols, "Preamble symbols") + # Generate the preamble_symbols and write them in the appropriate file --------------------------------------------- + preamble_symbols = transmitter_helper.generate_preamble_to_transmit(len(data_symbols)) + # ------------------------------------------------------------------------------------------------------------------ - # Concatenate the data symbols with the preamble symbols at the beginning and at the end - p_data_p_symbols = [] - for i in range(len(data_symbols)): - p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) - print("Total symbols: {}".format(p_data_p_symbols)) - print("Shape of total symbols: {}".format(np.shape(p_data_p_symbols))) + # Shape the preamble symbols and write the preamble samples in the preamble_samples file --------------------------- + transmitter_helper.shape_preamble_samples(h, preamble_symbols, USF) + # ------------------------------------------------------------------------------------------------------------------ - # Shape each of the symbols array - p_data_p_samples = [] - for i in range(len(p_data_p_symbols)): - p_data_p_samples.append(upfirdn(h, p_data_p_symbols[i], USF)) - - if params.logs: - print("Shaping the preamble and the data...") - print("Samples: {}".format(p_data_p_samples)) - print("Up-sampling factor: {}".format(params.USF)) - print("Shape of the total samples: {}".format(np.shape(p_data_p_samples))) - print("--------------------------------------------------------") - if params.plots: - for i in range(len(p_data_p_samples)): - plot_helper.samples_fft_plots(p_data_p_samples[i], "Samples {}".format(i), shift=True) + # Concatenate the data symbols with the preamble symbols at the beginning and at the end --------------------------- + p_data_p_symbols = transmitter_helper.concatenate_symbols(preamble_symbols, data_symbols) + # ------------------------------------------------------------------------------------------------------------------ - # Write the preamble samples in the preamble_samples file - preamble_samples = upfirdn(h, preamble_symbols, USF) - read_write.write_preamble_samples(preamble_samples) + # Shape each of the symbols array ---------------------------------------------------------------------------------- + p_data_p_samples = transmitter_helper.shape_symbols(h, p_data_p_symbols, USF) + # ------------------------------------------------------------------------------------------------------------------ - if params.logs: - print("Shaping the preamble...") - print("Number of samples for the preamble: {}".format(len(preamble_samples))) - print("--------------------------------------------------------") - if params.plots: - plot_helper.samples_fft_plots(preamble_samples, "Preamble samples", shift=True) - - # Choose the modulation frequencies - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 3: - modulating_frequencies = params.np.mean(params.FREQ_RANGES, axis=1) - elif params.MODULATION_TYPE == 2: - modulating_frequencies = [params.FREQ_RANGES[0][1], params.FREQ_RANGES[2][1]] - else: - raise ValueError("This mapping type does not exist yet... He he he") - - # Modulate the samples to fit in the required bands - if np.any(np.iscomplex(p_data_p_samples)): - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - p_data_p_samples = [item for sublist in p_data_p_samples for item in sublist] - p_data_p_modulated_samples = fourier_helper.modulate_complex_samples(p_data_p_samples, - modulating_frequencies) - elif params.MODULATION_TYPE == 3: - modulated_samples = [] - for i in range(len(p_data_p_samples)): - modulated_samples.append(fourier_helper.modulate_complex_samples(p_data_p_samples[i], - [modulating_frequencies[i]])) - p_data_p_modulated_samples = np.sum(modulated_samples, axis=0).flatten() - else: - raise ValueError("This mapping type does not exist yet... He he he") - - if params.logs: - print("Modulation of the signal...") - print("Minimum sample after modulation: {}".format(min(p_data_p_samples))) - print("Maximum sample after modulation: {}".format(max(p_data_p_samples))) - print("--------------------------------------------------------") - if params.plots: - plot_helper.samples_fft_plots(p_data_p_samples, "Samples to send", time=True, complex=True, shift=True) - else: - raise ValueError("TODO: handle real samples (e.g SSB)") + # Choose the modulation frequencies and modulate the samples ----------------------------------------------------- + p_data_p_modulated_samples = transmitter_helper.modulate_samples(p_data_p_samples) + # ------------------------------------------------------------------------------------------------------------------ # Scale the signal to the range [-1, 1] (with a bit of uncertainty margin, according to params.ABS_SAMPLE_RANGE) - samples_to_send = p_data_p_modulated_samples / (np.max(np.abs(p_data_p_modulated_samples))) * params.\ - ABS_SAMPLE_RANGE - - if params.logs: - print("Scaling the signal...") - print("Number of samples: {}".format(len(samples_to_send))) - print("Minimum sample after scaling: {}".format(min(samples_to_send))) - print("Maximum sample after scaling: {}".format(max(samples_to_send))) - print("--------------------------------------------------------") + samples_to_send = transmitter_helper.scale_samples(p_data_p_modulated_samples) return samples_to_send diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index e69de29..7191e25 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -0,0 +1,142 @@ +import numpy as np +from scipy.signal import upfirdn + +import params +import read_write +import preambles +import plot_helper +import fourier_helper + + +def generate_preamble_to_transmit(len_data_symbols): + if params.logs: + print("Generating the preamble...") + + preambles.generate_preamble_symbols(len_data_symbols) + preamble_symbols = read_write.read_preamble_symbols() + + if params.plots: + plot_helper.plot_complex_symbols(preamble_symbols, "Preamble symbols") + if params.logs: + print("Preamble symbols:\n{}".format(preamble_symbols)) + print("--------------------------------------------------------") + return preamble_symbols + + +def concatenate_symbols(preamble_symbols, data_symbols): + if params.logs: + print("Concatenating everything together (preamble-data-flipped preamble)...") + + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + p_data_p_symbols = np.concatenate((preamble_symbols, data_symbols, preamble_symbols[::-1])) + if params.logs: + print("Total symbols: {}".format(p_data_p_symbols)) + print("Number of total symbols: {}".format(np.shape(p_data_p_symbols))) + elif params.MODULATION_TYPE == 3: + p_data_p_symbols = [] + for i in range(len(data_symbols)): + p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) + if params.logs: + for i in range(len(p_data_p_symbols)): + print("Total symbols {}: {}".format(i, p_data_p_symbols)) + print("Number of total symbols {}: {}".format(i, np.shape(p_data_p_symbols))) + if params.plots: + plot_helper.plot_complex_symbols(p_data_p_symbols[i], "Symbols {}".format(i)) + else: + raise ValueError("This mapping type does not exist yet... He he he") + return p_data_p_symbols + + +def shape_symbols(h, p_data_p_symbols, USF): + if params.logs: + print("Pulse shaping the symbols...") + + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + p_data_p_samples = upfirdn(h, p_data_p_symbols, USF) + if params.logs: + print("Samples: {}".format(p_data_p_samples)) + print("Up-sampling factor: {}".format(params.USF)) + print("Number of samples: {}".format(len(p_data_p_samples))) + if params.plots: + plot_helper.samples_fft_plots(p_data_p_samples, "Samples after the pulse shaping", shift=True) + elif params.MODULATION_TYPE == 3: + p_data_p_samples = [] + for i in range(len(p_data_p_symbols)): + p_data_p_samples.append(upfirdn(h, p_data_p_symbols[i], USF)) + if params.plots: + for i in range(len(p_data_p_samples)): + plot_helper.samples_fft_plots(p_data_p_samples[i], "Samples {} after the pulse shaping".format(i), + shift=True) + else: + raise ValueError("This mapping type does not exist yet... He he he") + + if params.logs: + print("--------------------------------------------------------") + return p_data_p_samples + + +def shape_preamble_samples(h, preamble_symbols, USF): + if params.logs: + print("Shaping the preamble...") + + preamble_samples = upfirdn(h, preamble_symbols, USF) + read_write.write_preamble_samples(preamble_samples) + + if params.logs: + print("Number of samples for the preamble: {}".format(len(preamble_samples))) + if params.plots: + plot_helper.samples_fft_plots(preamble_samples, "Preamble samples", shift=True) + if params.logs: + print("--------------------------------------------------------") + return None + + +def modulate_samples(p_data_p_samples): + if params.logs: + print("Choosing the modulation frequencies and modulating the samples...") + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 3: + modulating_frequencies = params.np.mean(params.FREQ_RANGES, axis=1) + elif params.MODULATION_TYPE == 2: + modulating_frequencies = [params.FREQ_RANGES[0][1], params.FREQ_RANGES[2][1]] + else: + raise ValueError("This mapping type does not exist yet... He he he") + + # Modulate the samples to fit in the required bands + if np.any(np.iscomplex(p_data_p_samples)): + if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + p_data_p_samples = [item for sublist in p_data_p_samples for item in sublist] + p_data_p_modulated_samples = fourier_helper.modulate_complex_samples(p_data_p_samples, + modulating_frequencies) + if params.logs: + print("Min and max sample after modulation: ({}, {})".format(min(p_data_p_samples), + max(p_data_p_samples))) + if params.plots: + plot_helper.samples_fft_plots(p_data_p_samples, "Samples to send", time=True, complex=True, shift=True) + elif params.MODULATION_TYPE == 3: + modulated_samples = [] + for i in range(len(p_data_p_samples)): + modulated_samples.append(fourier_helper.modulate_complex_samples(p_data_p_samples[i], + [modulating_frequencies[i]])) + p_data_p_modulated_samples = np.sum(modulated_samples, axis=0).flatten() + else: + raise ValueError("This mapping type does not exist yet... He he he") + else: + raise ValueError("TODO: handle real samples (e.g SSB)") + if params.logs: + print("--------------------------------------------------------") + return p_data_p_modulated_samples + + +def scale_samples(p_data_p_modulated_samples): + if params.logs: + print("Scaling the samples to the server constraints...") + samples_to_send = p_data_p_modulated_samples / (np.max(np.abs(p_data_p_modulated_samples))) * params.\ + ABS_SAMPLE_RANGE + + if params.logs: + print("Scaling the signal...") + print("Number of samples: {}".format(len(samples_to_send))) + print("Minimum sample after scaling: {}".format(min(samples_to_send))) + print("Maximum sample after scaling: {}".format(max(samples_to_send))) + print("--------------------------------------------------------") + return samples_to_send From a992f9ac2a443d291a7af792f65775940a6021ac Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 18:57:20 +0200 Subject: [PATCH 08/20] Make the transmitter work for any modulation type --- src/params.py | 2 +- src/transmitter_helper.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/params.py b/src/params.py index 4e545c1..2ede442 100644 --- a/src/params.py +++ b/src/params.py @@ -45,7 +45,7 @@ def choose_symbol_period(): M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MODULATION_TYPE = 3 +MODULATION_TYPE = 1 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) # 3 = parity check approach (only works for M=4 here) diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 7191e25..2947848 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -28,7 +28,7 @@ def concatenate_symbols(preamble_symbols, data_symbols): print("Concatenating everything together (preamble-data-flipped preamble)...") if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - p_data_p_symbols = np.concatenate((preamble_symbols, data_symbols, preamble_symbols[::-1])) + p_data_p_symbols = np.concatenate((preamble_symbols, data_symbols[0], preamble_symbols[::-1])) if params.logs: print("Total symbols: {}".format(p_data_p_symbols)) print("Number of total symbols: {}".format(np.shape(p_data_p_symbols))) @@ -104,7 +104,6 @@ def modulate_samples(p_data_p_samples): # Modulate the samples to fit in the required bands if np.any(np.iscomplex(p_data_p_samples)): if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - p_data_p_samples = [item for sublist in p_data_p_samples for item in sublist] p_data_p_modulated_samples = fourier_helper.modulate_complex_samples(p_data_p_samples, modulating_frequencies) if params.logs: From 528f95f455a55633becbf771fa43b015f6fb15ce Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 19:26:22 +0200 Subject: [PATCH 09/20] Transmitter AND receiver (almost, not the decoding part) work for all MOD --- src/params.py | 2 +- src/receiver.py | 2 +- src/transmitter.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/params.py b/src/params.py index 2ede442..4e545c1 100644 --- a/src/params.py +++ b/src/params.py @@ -45,7 +45,7 @@ def choose_symbol_period(): M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MODULATION_TYPE = 1 +MODULATION_TYPE = 3 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) # 3 = parity check approach (only works for M=4 here) diff --git a/src/receiver.py b/src/receiver.py index 26c5705..bb794a8 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -80,7 +80,7 @@ def received_from_server(): # Load the input and output samples from their respective files input_samples = np.loadtxt(params.input_sample_file_path) # TODO: put output again - received_samples = np.loadtxt(params.output_sample_file_path) + received_samples = np.loadtxt(params.input_sample_file_path) # Plot the input and output samples in Time domain and Frequency domain if params.plots: diff --git a/src/transmitter.py b/src/transmitter.py index af0faab..0540931 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -12,6 +12,8 @@ import transmitter_helper +# TODO: refactor and make modular (PAM not handled yet) +# TODO: make it simple with transmitter helper def encoder(mapping): """ Encode a message into a sequence of symbols according to the given mapping @@ -39,7 +41,6 @@ def encoder(mapping): # Make a new string with these cropped bytes new_bits = ''.join(new_bits) - # TODO: refactor and make modular (PAM not handled yet) if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: # New structure with bits_per_symbol bits by row new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] @@ -94,7 +95,6 @@ def encoder(mapping): else: raise ValueError("This modulation type does not exist yet... He he he") - # TODO: make it work for PAM corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) for i in range(len(ints)): print(np.shape(ints)) From 1b7ce12eedcc2d1206cfc8fd025595a9bfc382a1 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 21:04:33 +0200 Subject: [PATCH 10/20] Restructured (almost) receiver works for MOD 1 --- src/local_test.py | 8 +- src/main.py | 5 +- src/params.py | 34 ++-- src/receiver.py | 412 +++++++------------------------------- src/receiver_helper.py | 346 ++++++++++++++++++++++++++++++++ src/transmitter.py | 115 +---------- src/transmitter_helper.py | 109 +++++++++- 7 files changed, 550 insertions(+), 479 deletions(-) create mode 100644 src/receiver_helper.py diff --git a/src/local_test.py b/src/local_test.py index f9b86db..f685e76 100644 --- a/src/local_test.py +++ b/src/local_test.py @@ -180,9 +180,9 @@ def local_test(): plot_helper.fft_plot(total_samples, "Total samples in Frequency domain", shift=True) # Modulate the total_samples - if params.MODULATION_TYPE == 1: + if params.MOD == 1: samples = fourier_helper.modulate_complex_samples(total_samples, params.np.mean(params.FREQ_RANGES, axis=1)) - elif params.MODULATION_TYPE == 2: + elif params.MOD == 2: samples = fourier_helper.modulate_complex_samples(total_samples, [params.FREQ_RANGES[0][1], params.FREQ_RANGES[2][1]]) else: @@ -223,12 +223,12 @@ def local_test(): print("Removed frequency range: {} (range {})".format(removed_freq_range, removed_freq_range + 1)) # Choose a frequency for demodulation - if params.MODULATION_TYPE == 1: + if params.MOD == 1: if removed_freq_range == 0: fc = np.mean(params.FREQ_RANGES[1]) else: fc = np.mean(params.FREQ_RANGES[0]) - elif params.MODULATION_TYPE == 2: + elif params.MOD == 2: if removed_freq_range == 0 or removed_freq_range == 1: fc = 7000 else: diff --git a/src/main.py b/src/main.py index 508f6b3..696ef6f 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,6 @@ import sys import time -import mappings import params import pulses import read_write @@ -19,11 +18,11 @@ params.params_log() # Transmitter - symbols = transmitter.encoder(mappings.choose_mapping()) + symbols = transmitter.encoder() _, h = pulses.root_raised_cosine() samples_to_send = transmitter.waveform_former(h, symbols) read_write.write_samples(samples_to_send) transmitter.send_samples() # Receiver - receiver.received_from_server() + received_symbols = receiver.n_tuple_former() diff --git a/src/params.py b/src/params.py index 4e545c1..a177e08 100644 --- a/src/params.py +++ b/src/params.py @@ -2,12 +2,12 @@ def choose_symbol_period(): - if MODULATION_TYPE == 1: - return np.floor(((1 + BETA) / MODULATION_TYPE_1_BANDWIDTH) * Fs) / Fs - elif MODULATION_TYPE == 2: - return np.floor(((1 + BETA) / MODULATION_TYPE_2_BANDWIDTH) * Fs) / Fs - elif MODULATION_TYPE == 3: - return np.floor(((1 + BETA) / MODULATION_TYPE_3_BANDWIDTH) * Fs) / Fs + if MOD == 1: + return np.floor(((1 + BETA) / MOD_1_BANDWIDTH) * Fs) / Fs + elif MOD == 2: + return np.floor(((1 + BETA) / MOD_2_BANDWIDTH) * Fs) / Fs + elif MOD == 3: + return np.floor(((1 + BETA) / MOD_3_BANDWIDTH) * Fs) / Fs else: raise ValueError('This modulation type does not exist yet... He he he') @@ -45,13 +45,13 @@ def choose_symbol_period(): M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MODULATION_TYPE = 3 +MOD = 1 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) # 3 = parity check approach (only works for M=4 here) -MODULATION_TYPE_1_BANDWIDTH = 1900 -MODULATION_TYPE_2_BANDWIDTH = 4000 -MODULATION_TYPE_3_BANDWIDTH = 1900 +MOD_1_BANDWIDTH = 2000 +MOD_2_BANDWIDTH = 4000 +MOD_3_BANDWIDTH = 2000 BETA = 0.2 # rolloff factor of our root-raised-cosine pulse (usually between 0.2 and 0.3 (said Prandoni)) T = choose_symbol_period() # symbol period (in seconds), i.e time before we can repeat the pulse while satisfying @@ -77,13 +77,13 @@ def params_log(): print("M = {}".format(M)) print("Normalized mapping: {}\n".format(NORMALIZE_MAPPING)) - print("Modulation type: {}".format(MODULATION_TYPE)) - if MODULATION_TYPE == 1: - bandwidth = MODULATION_TYPE_1_BANDWIDTH - elif MODULATION_TYPE == 2: - bandwidth = MODULATION_TYPE_2_BANDWIDTH - elif MODULATION_TYPE == 3: - bandwidth = MODULATION_TYPE_3_BANDWIDTH + print("Modulation type: {}".format(MOD)) + if MOD == 1: + bandwidth = MOD_1_BANDWIDTH + elif MOD == 2: + bandwidth = MOD_2_BANDWIDTH + elif MOD == 3: + bandwidth = MOD_3_BANDWIDTH else: bandwidth = "?" print("Bandwidth of the pulse: {} Hz\n".format(bandwidth)) diff --git a/src/receiver.py b/src/receiver.py index bb794a8..812f4ff 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -2,382 +2,109 @@ import time import sys -import fourier_helper import helper -import mappings -import parameter_estim import params -import plot_helper -import pulses import read_write +import mappings +import receiver_helper -# TODO: merge both methods -def decoder(y, mapping): - """ - Map the received symbols to the closest symbols of our mapping - :param y: the observation vector, i.e the received symbols - :param mapping: the chosen mapping for the communication - :return: integers between 0 and M-1, i.e integers corresponding to the bits sent - """ - # Number of symbols in the mapping - M = len(mapping) - # Number of symbols received - S = len(y) +def n_tuple_former(): + # Prepare the data + samples_received, preamble_samples_sent = receiver_helper.prepare_data() - distances = np.transpose(abs(np.tile(y, (M, 1)) - np.tile(mapping, (1, S)))) - ints = np.argmin(distances, 1) - if params.logs: - print("Equivalent integers:\n{}".format(ints)) - return ints + # Find the frequency range that has been removed + removed_freq_range, frequency_ranges_available, indices_available = receiver_helper.find_removed_frequency( + samples_received) + # Demodulation + demodulated_samples = receiver_helper.demodulate(samples_received, removed_freq_range, frequency_ranges_available, + indices_available) -def ints_to_message(ints): - """ - Map the integers (i.e indices in our mapping) to the received message - :param ints: integers between 0 and M-1, i.e integers corresponding to the bits sent - :return: the corresponding received message - """ + # Low pass + y = receiver_helper.low_pass(demodulated_samples, indices_available) - # Convert the ints to BITS_PER_SYMBOL bits - bits = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] - if params.logs: - print("Groups of BITS_PER_SYMBOL bits representing each integer:\n{}".format(bits)) + # Find the delay + delay = receiver_helper.find_delay(y, preamble_samples_sent, frequency_ranges_available) - # Make a new string with it - bits = ''.join(bits) - if params.logs: - print("Bits grouped all together:\n{}".format(bits)) + # Extract the preamble samples + preamble_samples_received = receiver_helper.extract_preamble_samples(y, delay, preamble_samples_sent, + frequency_ranges_available, indices_available) - # Slice the string into substrings of 7 characters - bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] - if params.logs: - print("Groups of 7 bits:\n{}".format(bits)) + # Compute the phase shift and the scaling factor + phase_shift_estim, scaling_factor_estim = receiver_helper.estimate_parameters(preamble_samples_sent, + preamble_samples_received, + indices_available) - # Add a zero at the beginning of each substring (cf transmitter) - new_bits = [] - for sub_string in bits: - new_bits.append('0' + sub_string) - if params.logs: - print("Groups of 8 bits (0 added at the beginning, cf. transmitter):\n{}".format(new_bits)) + # Crop the samples (remove the delay, the preamble, and adjust to the first relevant sample of data) + data_samples = receiver_helper.crop_samples_1(y, delay, len(preamble_samples_sent), indices_available) - # Convert from array of bytes to string - message_received = ''.join(helper.bits2string(new_bits)) + # Find the second_preamble_index + second_preamble_index = receiver_helper.find_second_preamble_index(data_samples, preamble_samples_sent) - message_sent = read_write.read_message_sent() - print("Message sent: {}".format(message_sent)) - print("Message received: {}".format(message_received)) - helper.compare_messages(message_sent, message_received) + # Crop the samples (remove the garbage at the end) + data_samples = receiver_helper.crop_samples_2(data_samples, second_preamble_index) - return message_received + # Correct the phase shift on the data samples + data_samples = receiver_helper.correct_params(data_samples, phase_shift_estim) + # Down-sample the samples to obtain the symbols + data_symbols = receiver_helper.down_sample(data_samples) -# TODO: Slice it in smaller methods -def received_from_server(): - # Prepare the data ------------------------------------------------------------------------------------------------- - if params.logs: - print("Preparing the data...") - # Load the input and output samples from their respective files - input_samples = np.loadtxt(params.input_sample_file_path) - # TODO: put output again - received_samples = np.loadtxt(params.input_sample_file_path) + return data_symbols - # Plot the input and output samples in Time domain and Frequency domain - if params.plots: - plot_helper.samples_fft_plots(input_samples, "Sent samples", complex=False) - plot_helper.samples_fft_plots(received_samples, "Received samples", complex=False) - # Read the preamble samples saved previously - preamble_samples = read_write.read_preamble_samples() - len_preamble_samples = len(preamble_samples) - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Find the frequency range that has been removed ------------------------------------------------------------------- - if params.logs: - print("Finding the frequency range that has been removed...") - range_indices, removed_freq_range = fourier_helper.find_removed_freq_range(received_samples) - if params.logs: - print("Removed frequency range: {} (range {})".format(removed_freq_range, removed_freq_range + 1)) - - # Array of the form [True, True, False, True], where False means that the 3rd frequency range is removes here - frequency_ranges_available = [True if i != removed_freq_range else False for i in range(len(params.FREQ_RANGES))] - indices_available = [] - for i in range(len(frequency_ranges_available)): - if frequency_ranges_available[i]: - indices_available.append(i) - if params.logs: - print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) - print("Available indices array: {}".format(indices_available)) - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Demodulation ----------------------------------------------------------------------------------------------------- +def decoder(symbols, mapping): + """ + Map the received symbols to the closest symbols of our mapping + :param symbols: the observation vector, i.e the received symbols + :param mapping: the chosen mapping for the communication + :return: integers between 0 and M-1, i.e integers corresponding to the bits sent + """ if params.logs: - print("Demodulation...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - if params.MODULATION_TYPE == 1: - fc = np.mean(params.FREQ_RANGES[frequency_ranges_available.index(True)]) + print("Decoding the symbols...") + if params.MOD == 1 or params.MOD == 2: + ints = receiver_helper.symbols_to_ints(symbols, mapping) - # TODO: improve this in term of the frequency_ranges_available array above - else: - if removed_freq_range == 0 or removed_freq_range == 1: - fc = np.mean([params.FREQ_RANGES[2][0], params.FREQ_RANGES[3][1]]) - else: - fc = np.mean([params.FREQ_RANGES[0][0], params.FREQ_RANGES[2][1]]) if params.logs: - print("Chosen fc for demodulation: {}".format(fc)) - - demodulated_samples = fourier_helper.demodulate(received_samples, fc) - if params.plots: - plot_helper.samples_fft_plots(demodulated_samples, "Demodulated received samples", shift=True) + print("Equivalent integers:\n{}".format(ints)) - elif params.MODULATION_TYPE == 3: - demodulated_samples = [] - demodulation_frequencies = np.mean(params.FREQ_RANGES, axis=1) - for i in range(len(params.FREQ_RANGES)): - if frequency_ranges_available[i]: - demodulated_samples.append(fourier_helper.demodulate(received_samples, demodulation_frequencies[i])) + # Convert the ints to BITS_PER_SYMBOL bits + bits = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] if params.logs: - print("Demodulation frequencies: {}".format(demodulation_frequencies)) - if params.plots: - for i in range(len(indices_available)): - plot_helper.samples_fft_plots( - demodulated_samples[i], - "Demodulated received samples {}".format(indices_available[i]), shift=True) - - else: - raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Low pass --------------------------------------------------------------------------------------------------------- - if params.logs: - print("Low passing...") - _, h = pulses.root_raised_cosine() - half_span_h = int(params.SPAN / 2) - h_matched = np.conjugate(h[::-1]) + print("Groups of BITS_PER_SYMBOL bits representing each integer:\n{}".format(bits)) - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - y = np.convolve(demodulated_samples, h_matched) - if params.plots: - plot_helper.samples_fft_plots(y, "Low-passed samples", shift=True) - if params.logs: - print("Length of y: {}".format(len(y))) - elif params.MODULATION_TYPE == 3: - y = [] - for i in range(len(demodulated_samples)): - y.append(np.convolve(demodulated_samples[i], h_matched)) - for i in range(len(y)): - plot_helper.samples_fft_plots( - y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) + # Make a new string with it + bits = ''.join(bits) if params.logs: - print("Y shape:") - for i in range(len(y)): - print(np.shape(y[i])) - else: - raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ + print("Bits grouped all together:\n{}".format(bits)) - # Find the delay --------------------------------------------------------------------------------------------------- - if params.logs: - print("Finding the delay...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - delay = parameter_estim.ML_theta_estimation(y, preamble_samples) - elif params.MODULATION_TYPE == 3: - delays = [] - for i in range(len(y)): - if frequency_ranges_available[i]: - delays.append(parameter_estim.ML_theta_estimation(y[i], preamble_samples)) - delay = int(np.round(np.mean(delays))) - if params.plots: - plot_helper.delay_plots(y, delay, "Delays estimated (only real part is shown)") - else: - raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print("Delay: {} samples".format(delay)) - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Extract the preamble samples ------------------------------------------------------------------------------------- - if params.logs: - print("Extracting the preamble samples...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - preamble_samples_received = y[delay:delay + len_preamble_samples] - if params.plots: - plot_helper.two_simple_plots(np.real(preamble_samples_received), np.real(preamble_samples), - "Preamble samples received vs preamble samples sent", "received", "expected") + # Slice the string into substrings of 7 characters + bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] if params.logs: - print("Number of samples for the actual preamble: {} samples".format(len_preamble_samples)) - print("Number of samples for the received preamble: {} samples".format(len(preamble_samples_received))) - elif params.MODULATION_TYPE == 3: - preamble_samples_received = [] - for i in range(len(y)): - preamble_samples_received.append(y[i][delay:delay + len_preamble_samples]) - if params.plots: - for i in range(len(preamble_samples_received)): - if frequency_ranges_available[i]: - plot_helper.compare_preambles(preamble_samples_received[i], preamble_samples, - "Preamble samples received {} vs preamble samples sent" - .format(indices_available[i])) - else: - raise ValueError('This modulation type does not exist yet... He he he') + print("Groups of 7 bits:\n{}".format(bits)) - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Compute the phase shift and the scaling factor ------------------------------------------------------------------- - if params.logs: - print("Computing the phase shift and the scaling factor...") - # Remove SPAN/2 samples in the end because there is still data there for the received preamble - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - phase_shift_estim, scaling_factor_estim = parameter_estim.ML_phase_scaling_estim( - preamble_samples[:len_preamble_samples - half_span_h], - preamble_samples_received[:len(preamble_samples_received) - half_span_h]) - if params.logs: - print("Phase shift: {}".format(phase_shift_estim)) - print("Scaling factor: {}".format(scaling_factor_estim)) - elif params.MODULATION_TYPE == 3: - phase_shift_estim_array = [] - scaling_factor_estim_array = [] - for i in range(len(preamble_samples_received)): - phase_shift_estim_in_range, scaling_factor_estim_in_range = parameter_estim.ML_phase_scaling_estim( - preamble_samples[:len_preamble_samples - half_span_h], - preamble_samples_received[i][:len(preamble_samples_received[i]) - half_span_h]) - phase_shift_estim_array.append(phase_shift_estim_in_range) - scaling_factor_estim_array.append(scaling_factor_estim_in_range) + # Add a zero at the beginning of each substring (cf transmitter) + new_bits = [] + for sub_string in bits: + new_bits.append('0' + sub_string) if params.logs: - for i in range(len(phase_shift_estim_array)): - print("Phase shift {}: {}".format(indices_available[i], phase_shift_estim_array[i])) - print("Scaling factor {}: {}".format(indices_available[i], scaling_factor_estim_array[i])) - else: - raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Crop the samples (remove the delay, the preamble, and adjust to the first relevant sample of data) --------------- - if params.logs: - print("Cropping the samples (removing the delay, the preamble, " - "and adjusting to the first relevant sample of data)...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - data_samples = y[delay + len_preamble_samples - half_span_h + params.USF:] - if params.plots: - plot_helper.plot_complex_function(y, "y before removing anything") - plot_helper.plot_complex_function(data_samples, "y after removing the delay, the preamble, and adjusting") - elif params.MODULATION_TYPE == 3: - data_samples = [] - for i in range(len(y)): - data_samples.append(y[i][delay + len_preamble_samples - 1 - half_span_h + 1 + params.USF:]) - if params.plots: - for i in range(len(data_samples)): - plot_helper.plot_complex_function(data_samples[i], - "y[{}] after removing the delay, the preamble, and adjusting". - format(indices_available[i])) - else: - raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print(params.USF) - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Find the second_preamble_index ----------------------------------------------------------------------------------- - if params.logs: - print("Finding the second preamble index...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - second_preamble_index = parameter_estim.ML_theta_estimation(data_samples, - preamble_samples=preamble_samples[::-1]) - elif params.MODULATION_TYPE == 3: - second_preamble_index = [] - for i in range(len(data_samples)): - second_preamble_index.append(parameter_estim.ML_theta_estimation( - data_samples[i], preamble_samples=preamble_samples[::-1])) - second_preamble_index = int(np.round(np.mean(second_preamble_index))) - else: - raise ValueError('This modulation type does not exist yet... He he he') - if params.logs: - print("Second preamble index: {} samples".format(second_preamble_index)) - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ + print("Groups of 8 bits (0 added at the beginning, cf. transmitter):\n{}".format(new_bits)) - # Crop the samples (remove the garbage at the end) ----------------------------------------------------------------- - if params.logs: - print("Cropping the samples (removing the garbage at the end)...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - data_samples = data_samples[:second_preamble_index + half_span_h - params.USF + 1] - if params.plots: - plot_helper.plot_complex_function(data_samples, "y (only data)") - elif params.MODULATION_TYPE == 3: - for i in range(len(data_samples)): - data_samples[i] = data_samples[i][:second_preamble_index + half_span_h - params.USF + 1] - if params.plots: - for i in range(len(data_samples)): - plot_helper.plot_complex_function(data_samples[i], "y (only data)") - else: - raise ValueError("This modulation type does not exist yet... He he he") - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Correct the phase shift on the data samples ---------------------------------------------------------------------- - if params.logs: - print("Correcting the phase shift and the scaling factor (not yet) on the data samples...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - data_samples = data_samples * np.exp(-1j * (phase_shift_estim - np.pi / 2)) - elif params.MODULATION_TYPE == 3: - print(len(data_samples)) - for i in range(len(data_samples)): - data_samples[i] = data_samples[i] * np.exp(-1j * (phase_shift_estim_array[i] - np.pi / 2)) - else: - raise ValueError("This modulation type does not exist yet... He he he") - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ + # Convert from array of bytes to string + message_received = ''.join(helper.bits2string(new_bits)) - # Down-sample the samples to obtain the symbols -------------------------------------------------------------------- - if params.logs: - print("Down-sampling...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - data_symbols = data_samples[::params.USF] - if params.logs: - print("Number of symbols received: {}".format(len(data_symbols))) - if params.plots: - plot_helper.plot_complex_function(data_symbols, "y without preamble") - plot_helper.plot_complex_symbols(data_symbols, "Symbols received", annotate=False) - elif params.MODULATION_TYPE == 3: - data_symbols = [] - for i in range(len(data_samples)): - data_symbols.append(data_samples[i][::params.USF]) - if params.logs: - print("Shape of the received symbols: {}".format(np.shape(data_symbols))) - if params.plots: - for i in range(len(data_symbols)): - plot_helper.plot_complex_function(data_symbols[i], "y without preamble") - plot_helper.plot_complex_symbols(data_symbols[i], "Symbols received", annotate=False) - else: - raise ValueError("This modulation type does not exist yet... He he he") - if params.logs: - print("--------------------------------------------------------") - # ------------------------------------------------------------------------------------------------------------------ - - # Decode the symbols ----------------------------------------------------------------------------------------------- - if params.logs: - print("Decoding the symbols...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - ints = decoder(data_symbols, np.asarray(mappings.choose_mapping())) - message_received = ints_to_message(ints) - read_write.write_message_received(message_received) - # elif params.MODULATION_TYPE == 3: - # TODO + message_sent = read_write.read_message_sent() + print("Message sent: {}".format(message_sent)) + print("Message received: {}".format(message_received)) + helper.compare_messages(message_sent, message_received) + elif params.MOD == 3: + return None else: raise ValueError("This modulation type does not exist yet... He he he") if params.logs: print("--------------------------------------------------------") + return message_received # Intended for testing (to run the program, run main.py) @@ -386,6 +113,7 @@ def received_from_server(): moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) log_file = open("../logs/" + moment + ".log", "w+") sys.stdout = log_file - received_from_server() -# TODO: make sure the scaling factor, delay, phase shift must be the same for 3 freq ranges + symbols = n_tuple_former() + message = decoder(symbols, mappings.choose_mapping()) + read_write.write_message_received(message) diff --git a/src/receiver_helper.py b/src/receiver_helper.py new file mode 100644 index 0000000..1b95627 --- /dev/null +++ b/src/receiver_helper.py @@ -0,0 +1,346 @@ +import numpy as np + +import params +import plot_helper +import read_write +import fourier_helper +import pulses +import parameter_estim + + +def prepare_data(): + if params.logs: + print("Preparing the data...") + # Load the input and output samples from their respective files + input_samples = np.loadtxt(params.input_sample_file_path) + # TODO: put output again + samples_received = np.loadtxt(params.output_sample_file_path) + + # Plot the input and output samples in Time domain and Frequency domain + if params.plots: + plot_helper.samples_fft_plots(input_samples, "Sent samples", complex=False) + plot_helper.samples_fft_plots(samples_received, "Received samples", complex=False) + + # Read the preamble samples saved previously + preamble_samples_sent = read_write.read_preamble_samples() + if params.logs: + print("--------------------------------------------------------") + return samples_received, preamble_samples_sent + + +def find_removed_frequency(samples_received): + if params.logs: + print("Finding the frequency range that has been removed...") + _, removed_freq_range = fourier_helper.find_removed_freq_range(samples_received) + if params.logs: + print("Removed frequency range: {} (range {})".format(removed_freq_range, removed_freq_range + 1)) + + # Array of the form [True, True, False, True], where False means that the 3rd frequency range is removes here + frequency_ranges_available = [True if i != removed_freq_range else False for i in range(len(params.FREQ_RANGES))] + indices_available = [] + for i in range(len(frequency_ranges_available)): + if frequency_ranges_available[i]: + indices_available.append(i) + if params.logs: + print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) + print("Available indices array: {}".format(indices_available)) + print("--------------------------------------------------------") + return removed_freq_range, frequency_ranges_available, indices_available + + +def demodulate(samples_received, removed_freq_range, frequency_ranges_available, indices_available): + if params.logs: + print("Demodulation...") + if params.MOD == 1 or params.MOD == 2: + if params.MOD == 1: + fc = np.mean(params.FREQ_RANGES[frequency_ranges_available.index(True)]) + + # TODO: improve this in term of the frequency_ranges_available array above + else: + if removed_freq_range == 0 or removed_freq_range == 1: + fc = np.mean([params.FREQ_RANGES[2][0], params.FREQ_RANGES[3][1]]) + else: + fc = np.mean([params.FREQ_RANGES[0][0], params.FREQ_RANGES[2][1]]) + if params.logs: + print("Chosen fc for demodulation: {}".format(fc)) + + demodulated_samples = fourier_helper.demodulate(samples_received, fc) + if params.plots: + plot_helper.samples_fft_plots(demodulated_samples, "Demodulated received samples", shift=True) + + elif params.MOD == 3: + demodulated_samples = [] + demodulation_frequencies = np.mean(params.FREQ_RANGES, axis=1) + for i in range(len(params.FREQ_RANGES)): + if frequency_ranges_available[i]: + demodulated_samples.append(fourier_helper.demodulate(samples_received, demodulation_frequencies[i])) + if params.logs: + print("Demodulation frequencies: {}".format(demodulation_frequencies)) + if params.plots: + for i in range(len(indices_available)): + plot_helper.samples_fft_plots( + demodulated_samples[i], + "Demodulated received samples {}".format(indices_available[i]), shift=True) + + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("--------------------------------------------------------") + return demodulated_samples + + +def low_pass(demodulated_samples, indices_available): + if params.logs: + print("Low passing...") + _, h = pulses.root_raised_cosine() + h_matched = np.conjugate(h[::-1]) + + if params.MOD == 1 or params.MOD == 2: + y = np.convolve(demodulated_samples, h_matched) + if params.plots: + plot_helper.samples_fft_plots(y, "Low-passed samples", shift=True) + if params.logs: + print("Length of y: {}".format(len(y))) + elif params.MOD == 3: + y = [] + for i in range(len(demodulated_samples)): + y.append(np.convolve(demodulated_samples[i], h_matched)) + for i in range(len(y)): + plot_helper.samples_fft_plots( + y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) + if params.logs: + print("Y shape:") + for i in range(len(y)): + print(np.shape(y[i])) + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("--------------------------------------------------------") + return y + + +def find_delay(y, preamble_samples_sent, frequency_ranges_available): + if params.logs: + print("Finding the delay...") + if params.MOD == 1 or params.MOD == 2: + delay = parameter_estim.ML_theta_estimation(y, preamble_samples_sent) + elif params.MOD == 3: + delays = [] + for i in range(len(y)): + if frequency_ranges_available[i]: + delays.append(parameter_estim.ML_theta_estimation(y[i], preamble_samples_sent)) + delay = int(np.round(np.mean(delays))) + if params.plots: + plot_helper.delay_plots(y, delay, "Delays estimated (only real part is shown)") + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("Delay: {} samples".format(delay)) + print("--------------------------------------------------------") + return delay + + +def extract_preamble_samples(y, delay, preamble_samples_sent, frequency_ranges_available, indices_available): + if params.logs: + print("Extracting the preamble samples...") + len_preamble_samples_sent = len(preamble_samples_sent) + if params.MOD == 1 or params.MOD == 2: + preamble_samples_received = y[delay:delay + len_preamble_samples_sent] + if params.plots: + plot_helper.two_simple_plots(np.real(preamble_samples_received), np.real(preamble_samples_sent), + "Preamble samples received vs preamble samples sent", "received", "expected") + if params.logs: + print("Number of samples for the actual preamble: {} samples".format(len_preamble_samples_sent)) + print("Number of samples for the received preamble: {} samples".format(len(preamble_samples_received))) + elif params.MOD == 3: + preamble_samples_received = [] + for i in range(len(y)): + preamble_samples_received.append(y[i][delay:delay + len_preamble_samples_sent]) + if params.plots: + for i in range(len(preamble_samples_received)): + if frequency_ranges_available[i]: + plot_helper.compare_preambles(preamble_samples_received[i], preamble_samples_sent, + "Preamble samples received {} vs preamble samples sent" + .format(indices_available[i])) + else: + raise ValueError('This modulation type does not exist yet... He he he') + + if params.logs: + print("--------------------------------------------------------") + return preamble_samples_received + + +def estimate_parameters(preamble_samples_sent, preamble_samples_received, indices_available): + if params.logs: + print("Computing the phase shift and the scaling factor...") + # Remove SPAN/2 samples in the end because there is still data there for the received preamble + half_span_h = int(params.SPAN / 2) + len_preamble_samples_sent = len(preamble_samples_sent) + if params.MOD == 1 or params.MOD == 2: + phase_shift_estim, scaling_factor_estim = parameter_estim.ML_phase_scaling_estim( + preamble_samples_sent[:len_preamble_samples_sent - half_span_h], + preamble_samples_received[:len(preamble_samples_received) - half_span_h]) + if params.logs: + print("Phase shift: {}".format(phase_shift_estim)) + print("Scaling factor: {}".format(scaling_factor_estim)) + elif params.MOD == 3: + phase_shift_estim = [] + scaling_factor_estim = [] + for i in range(len(preamble_samples_received)): + phase_shift_estim_in_range, scaling_factor_estim_in_range = parameter_estim.ML_phase_scaling_estim( + preamble_samples_sent[:len_preamble_samples_sent - half_span_h], + preamble_samples_received[i][:len(preamble_samples_received[i]) - half_span_h]) + phase_shift_estim.append(phase_shift_estim_in_range) + scaling_factor_estim.append(scaling_factor_estim_in_range) + if params.logs: + for i in range(len(phase_shift_estim)): + print("Phase shift {}: {}".format(indices_available[i], phase_shift_estim[i])) + print("Scaling factor {}: {}".format(indices_available[i], scaling_factor_estim[i])) + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("--------------------------------------------------------") + return phase_shift_estim, scaling_factor_estim + + +def crop_samples_1(y, delay, len_preamble_samples_sent, indices_available): + if params.logs: + print("Cropping the samples (removing the delay, the preamble, " + "and adjusting to the first relevant sample of data)...") + half_span_h = int(params.SPAN/2) + if params.MOD == 1 or params.MOD == 2: + data_samples = y[delay + len_preamble_samples_sent - half_span_h + params.USF:] + if params.plots: + plot_helper.plot_complex_function(y, "y before removing anything") + plot_helper.plot_complex_function(data_samples, "y after removing the delay, the preamble, and adjusting") + elif params.MOD == 3: + data_samples = [] + for i in range(len(y)): + data_samples.append(y[i][delay + len_preamble_samples_sent - 1 - half_span_h + 1 + params.USF:]) + if params.plots: + for i in range(len(data_samples)): + plot_helper.plot_complex_function(data_samples[i], + "y[{}] after removing the delay, the preamble, and adjusting". + format(indices_available[i])) + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print(params.USF) + print("--------------------------------------------------------") + return data_samples + + +def find_second_preamble_index(data_samples, preamble_samples_sent): + if params.logs: + print("Finding the second preamble index...") + if params.MOD == 1 or params.MOD == 2: + second_preamble_index = parameter_estim.ML_theta_estimation(data_samples, + preamble_samples=preamble_samples_sent[::-1]) + elif params.MOD == 3: + second_preamble_index = [] + for i in range(len(data_samples)): + second_preamble_index.append(parameter_estim.ML_theta_estimation( + data_samples[i], preamble_samples=preamble_samples_sent[::-1])) + second_preamble_index = int(np.round(np.mean(second_preamble_index))) + else: + raise ValueError('This modulation type does not exist yet... He he he') + if params.logs: + print("Second preamble index: {} samples".format(second_preamble_index)) + print("--------------------------------------------------------") + return second_preamble_index + + +def crop_samples_2(data_samples, second_preamble_index): + if params.logs: + print("Cropping the samples (removing the garbage at the end)...") + half_span_h = int(params.SPAN/2) + if params.MOD == 1 or params.MOD == 2: + data_samples = data_samples[:second_preamble_index + half_span_h - params.USF + 1] + if params.plots: + plot_helper.plot_complex_function(data_samples, "y (only data)") + elif params.MOD == 3: + for i in range(len(data_samples)): + data_samples[i] = data_samples[i][:second_preamble_index + half_span_h - params.USF + 1] + if params.plots: + for i in range(len(data_samples)): + plot_helper.plot_complex_function(data_samples[i], "y (only data)") + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") + return data_samples + + +def correct_params(data_samples, phase_shift_estim): + if params.logs: + print("Correcting the phase shift and the scaling factor (not yet) on the data samples...") + if params.MOD == 1 or params.MOD == 2: + data_samples = data_samples * np.exp(-1j * (phase_shift_estim - np.pi / 2)) + elif params.MOD == 3: + for i in range(len(data_samples)): + data_samples[i] = data_samples[i] * np.exp(-1j * (phase_shift_estim[i] - np.pi / 2)) + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") + return data_samples + + +def down_sample(data_samples): + if params.logs: + print("Down-sampling...") + if params.MOD == 1 or params.MOD == 2: + data_symbols = data_samples[::params.USF] + if params.logs: + print("Number of symbols received: {}".format(len(data_symbols))) + if params.plots: + plot_helper.plot_complex_function(data_symbols, "y without preamble") + plot_helper.plot_complex_symbols(data_symbols, "Symbols received", annotate=False) + elif params.MOD == 3: + data_symbols = [] + for i in range(len(data_samples)): + data_symbols.append(data_samples[i][::params.USF]) + if params.logs: + print("Shape of the received symbols: {}".format(np.shape(data_symbols))) + if params.plots: + for i in range(len(data_symbols)): + plot_helper.plot_complex_function(data_symbols[i], "y without preamble") + plot_helper.plot_complex_symbols(data_symbols[i], "Symbols received", annotate=False) + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("--------------------------------------------------------") + return data_symbols + + +def symbols_to_ints(symbols, mapping): + if params.logs: + print("Associating symbols to integers (indices of our mapping)...") + # Make sure symbols and mapping have less or equal than 2 dimensions + if len(np.shape(symbols)) > 2 or len(np.shape(mapping)) > 2: + raise AttributeError("One of the vectors symbols and mapping has more than 2 dimensions!") + + # If symbols is a column vector, make it a row vector + n_elems_axis_0_y = np.size(symbols, 0) + if n_elems_axis_0_y != 1: + symbols = np.reshape(symbols, (1, n_elems_axis_0_y)) + else: + symbols = np.reshape(symbols, (1, np.size(symbols, 1))) + + # If mapping is a row vector, make it a column vector + if np.size(mapping, 0) == 1: + mapping = np.reshape(mapping, (np.size(mapping, 1), 1)) + else: + mapping = np.reshape(mapping, (np.size(mapping, 0), 1)) + + # Number of symbols in the mapping + M = len(mapping) + # Number of symbols received + S = len(symbols) + + distances = np.transpose(abs(np.tile(symbols, (M, 1)) - np.tile(mapping, (1, S)))) + if params.logs: + print("--------------------------------------------------------") + return np.argmin(distances, 1) + + diff --git a/src/transmitter.py b/src/transmitter.py index 0540931..8c74cec 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -3,111 +3,23 @@ import sys import numpy as np -import helper -import mappings import params -import plot_helper import pulses import read_write import transmitter_helper -# TODO: refactor and make modular (PAM not handled yet) -# TODO: make it simple with transmitter helper -def encoder(mapping): +# TODO: Make modular (PAM not handled yet) +def encoder(): """ Encode a message into a sequence of symbols according to the given mapping - :param mapping: the mapping used for transmission :return: the corresponding symbols for the message """ # Retrieve the message from file - message_file = open(params.input_message_file_path) - message = message_file.readline() - print("Sent message:\n{}".format(message)) - if params.logs: - print("Length: {} characters".format(len(message))) - print("--------------------------------------------------------") - - # Retrieve the message as a sequences of binary bytes - string_bytes = helper.string2bits(message) + message_bytes = transmitter_helper.retrieve_message_as_bytes() - if params.logs: - print("Corresponding bytes:\n{}".format(string_bytes)) - print("--------------------------------------------------------") - - # Remove the most significant bit (0) as it is useless in ASCII (do not forget to put it again in the receiver!) - new_bits = [b[1:] for b in string_bytes] - - # Make a new string with these cropped bytes - new_bits = ''.join(new_bits) - - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: - # New structure with bits_per_symbol bits by row - new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] - - # Convert this new bits sequence to an integer sequence - ints = [[int(b, 2) for b in new_bits]] - - if params.logs: - print("Cropped and re-structured bits:\n{}".format(new_bits)) - print("Equivalent integers (indices for our mapping):\n{}".format(ints)) - print("--------------------------------------------------------") - elif params.MODULATION_TYPE == 3: - # Choose the number of bit streams (depends on the number of frequency ranges) - n_bit_streams = len(params.FREQ_RANGES) - - # Choose the length of our bit streams - len_bit_streams = int(np.ceil(len(new_bits) / (n_bit_streams - 1))) - - # Make it even - if len_bit_streams % 2 != 0: - len_bit_streams = len_bit_streams + 1 - - # Construct the bit streams array with zeros - bit_streams = np.zeros((n_bit_streams, len_bit_streams), dtype=int) - - # Fill the bit streams arrays - for i in range(len(new_bits)): - bit_streams[i % (n_bit_streams - 1)][int(np.ceil(i / (n_bit_streams - 1)))] = new_bits[i] - - # Construct the parity check bit stream and insert it in the bit streams array - pc_bit_stream = np.sum(bit_streams[:n_bit_streams - 1], axis=0) - for i in range(len_bit_streams): - pc_bit_stream[i] = 0 if pc_bit_stream[i] % 2 == 0 else 1 - bit_streams[n_bit_streams - 1] = pc_bit_stream - - if params.logs: - print(" ") - print("Bit stream {}\n: {}".format(bit_streams.shape, bit_streams)) - print("--------------------------------------------------------") - - # Group them by groups of BITS_PER_SYMBOL bits - ints = np.zeros((n_bit_streams, int(len_bit_streams / 2)), dtype=str) - for i in range(n_bit_streams): - for j in range(int(len_bit_streams / params.BITS_PER_SYMBOL)): - grouped_bits = str(bit_streams[i][j]) + str(bit_streams[i][j + params.BITS_PER_SYMBOL - 1]) - mapping_index = int(grouped_bits, base=2) - ints[i][j] = mapping_index - - if params.logs: - print("Ints bits stream {}\n: {}".format(ints.shape, ints)) - print("--------------------------------------------------------") - else: - raise ValueError("This modulation type does not exist yet... He he he") - - corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) - for i in range(len(ints)): - print(np.shape(ints)) - corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] - - if params.logs: - print("Mapping the integers to the symbols in the mapping...") - print("Symbols/n-tuples to be sent:\n{}".format(corresponding_symbols)) - print("Shape of the symbols: {}".format(np.shape(corresponding_symbols))) - print("--------------------------------------------------------") - if params.plots: - plot_helper.plot_complex_symbols(corresponding_symbols, "{} data symbols to send" - .format(np.shape(corresponding_symbols)), "blue") + # Associate the message bytes to the corresponding symbols + corresponding_symbols = transmitter_helper.message_bytes_to_int(message_bytes) return np.asarray(corresponding_symbols) @@ -119,25 +31,20 @@ def waveform_former(h, data_symbols, USF=params.USF): :param USF: the up-sampling factor, i.e the number of samples per symbols, also called SPS :return: the samples of a modulated pulse train to send to the server """ - # Generate the preamble_symbols and write them in the appropriate file --------------------------------------------- + # Generate the preamble_symbols and write them in the appropriate file preamble_symbols = transmitter_helper.generate_preamble_to_transmit(len(data_symbols)) - # ------------------------------------------------------------------------------------------------------------------ - # Shape the preamble symbols and write the preamble samples in the preamble_samples file --------------------------- + # Shape the preamble symbols and write the preamble samples in the preamble_samples file transmitter_helper.shape_preamble_samples(h, preamble_symbols, USF) - # ------------------------------------------------------------------------------------------------------------------ - # Concatenate the data symbols with the preamble symbols at the beginning and at the end --------------------------- + # Concatenate the data symbols with the preamble symbols at the beginning and at the end p_data_p_symbols = transmitter_helper.concatenate_symbols(preamble_symbols, data_symbols) - # ------------------------------------------------------------------------------------------------------------------ - # Shape each of the symbols array ---------------------------------------------------------------------------------- + # Shape each of the symbols array p_data_p_samples = transmitter_helper.shape_symbols(h, p_data_p_symbols, USF) - # ------------------------------------------------------------------------------------------------------------------ - # Choose the modulation frequencies and modulate the samples ----------------------------------------------------- + # Choose the modulation frequencies and modulate the samples p_data_p_modulated_samples = transmitter_helper.modulate_samples(p_data_p_samples) - # ------------------------------------------------------------------------------------------------------------------ # Scale the signal to the range [-1, 1] (with a bit of uncertainty margin, according to params.ABS_SAMPLE_RANGE) samples_to_send = transmitter_helper.scale_samples(p_data_p_modulated_samples) @@ -168,7 +75,7 @@ def send_samples(): params.params_log() # Encode the message - symbols = encoder(mappings.choose_mapping()) + symbols = encoder() # Generate the root-raised_cosine _, h_pulse = pulses.root_raised_cosine() diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 2947848..9f84d10 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -6,6 +6,98 @@ import preambles import plot_helper import fourier_helper +import helper +import mappings + + +def retrieve_message_as_bytes(): + if params.logs: + print("Retrieving the message from the appropriate file...") + + message_file = open(params.input_message_file_path) + message = message_file.readline() + message_bytes = helper.string2bits(message) + new_bytes = [b[1:] for b in message_bytes] + new_message_bytes_grouped = ''.join(new_bytes) + + if params.logs: + print("Sent message:\n{}".format(message)) + print("Length: {} characters".format(len(message))) + print("Corresponding bytes:\n{}".format(message_bytes)) + print("New bytes:\n{}".format(new_bytes)) + print("--------------------------------------------------------") + return new_message_bytes_grouped + + +def message_bytes_to_int(new_bits): + if params.MOD == 1 or params.MOD == 2: + # New structure with bits_per_symbol bits by row + new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] + + # Convert this new bits sequence to an integer sequence + ints = [[int(b, 2) for b in new_bits]] + + if params.logs: + print("Cropped and re-structured bits:\n{}".format(new_bits)) + print("Equivalent integers (indices for our mapping):\n{}".format(ints)) + print("--------------------------------------------------------") + elif params.MOD == 3: + # Choose the number of bit streams (depends on the number of frequency ranges) + n_bit_streams = len(params.FREQ_RANGES) + + # Choose the length of our bit streams + len_bit_streams = int(np.ceil(len(new_bits) / (n_bit_streams - 1))) + + # Make it even + if len_bit_streams % 2 != 0: + len_bit_streams = len_bit_streams + 1 + + # Construct the bit streams array with zeros + bit_streams = np.zeros((n_bit_streams, len_bit_streams), dtype=int) + + # Fill the bit streams arrays + for i in range(len(new_bits)): + bit_streams[i % (n_bit_streams - 1)][int(np.ceil(i / (n_bit_streams - 1)))] = new_bits[i] + + # Construct the parity check bit stream and insert it in the bit streams array + pc_bit_stream = np.sum(bit_streams[:n_bit_streams - 1], axis=0) + for i in range(len_bit_streams): + pc_bit_stream[i] = 0 if pc_bit_stream[i] % 2 == 0 else 1 + bit_streams[n_bit_streams - 1] = pc_bit_stream + + if params.logs: + print(" ") + print("Bit stream {}:\n{}".format(bit_streams.shape, bit_streams)) + print("--------------------------------------------------------") + + # Group them by groups of BITS_PER_SYMBOL bits + ints = np.zeros((n_bit_streams, int(len_bit_streams / 2)), dtype=str) + for i in range(n_bit_streams): + for j in range(int(len_bit_streams / params.BITS_PER_SYMBOL)): + grouped_bits = str(bit_streams[i][j]) + str(bit_streams[i][j + params.BITS_PER_SYMBOL - 1]) + mapping_index = int(grouped_bits, base=2) + ints[i][j] = mapping_index + + if params.logs: + print("Ints bits stream {}:\n{}".format(ints.shape, ints)) + print("--------------------------------------------------------") + else: + raise ValueError("This modulation type does not exist yet... He he he") + + corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) + mapping = mappings.choose_mapping() + for i in range(len(ints)): + corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] + + if params.logs: + print("Mapping the integers to the symbols in the mapping...") + print("Symbols/n-tuples to be sent:\n{}".format(corresponding_symbols)) + print("Shape of the symbols: {}".format(np.shape(corresponding_symbols))) + print("--------------------------------------------------------") + if params.plots: + plot_helper.plot_complex_symbols(corresponding_symbols, "{} data symbols to send" + .format(np.shape(corresponding_symbols)), "blue") + return corresponding_symbols def generate_preamble_to_transmit(len_data_symbols): @@ -27,12 +119,12 @@ def concatenate_symbols(preamble_symbols, data_symbols): if params.logs: print("Concatenating everything together (preamble-data-flipped preamble)...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + if params.MOD == 1 or params.MOD == 2: p_data_p_symbols = np.concatenate((preamble_symbols, data_symbols[0], preamble_symbols[::-1])) if params.logs: print("Total symbols: {}".format(p_data_p_symbols)) print("Number of total symbols: {}".format(np.shape(p_data_p_symbols))) - elif params.MODULATION_TYPE == 3: + elif params.MOD == 3: p_data_p_symbols = [] for i in range(len(data_symbols)): p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) @@ -51,7 +143,7 @@ def shape_symbols(h, p_data_p_symbols, USF): if params.logs: print("Pulse shaping the symbols...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + if params.MOD == 1 or params.MOD == 2: p_data_p_samples = upfirdn(h, p_data_p_symbols, USF) if params.logs: print("Samples: {}".format(p_data_p_samples)) @@ -59,7 +151,7 @@ def shape_symbols(h, p_data_p_symbols, USF): print("Number of samples: {}".format(len(p_data_p_samples))) if params.plots: plot_helper.samples_fft_plots(p_data_p_samples, "Samples after the pulse shaping", shift=True) - elif params.MODULATION_TYPE == 3: + elif params.MOD == 3: p_data_p_samples = [] for i in range(len(p_data_p_symbols)): p_data_p_samples.append(upfirdn(h, p_data_p_symbols[i], USF)) @@ -94,16 +186,16 @@ def shape_preamble_samples(h, preamble_symbols, USF): def modulate_samples(p_data_p_samples): if params.logs: print("Choosing the modulation frequencies and modulating the samples...") - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 3: + if params.MOD == 1 or params.MOD == 3: modulating_frequencies = params.np.mean(params.FREQ_RANGES, axis=1) - elif params.MODULATION_TYPE == 2: + elif params.MOD == 2: modulating_frequencies = [params.FREQ_RANGES[0][1], params.FREQ_RANGES[2][1]] else: raise ValueError("This mapping type does not exist yet... He he he") # Modulate the samples to fit in the required bands if np.any(np.iscomplex(p_data_p_samples)): - if params.MODULATION_TYPE == 1 or params.MODULATION_TYPE == 2: + if params.MOD == 1 or params.MOD == 2: p_data_p_modulated_samples = fourier_helper.modulate_complex_samples(p_data_p_samples, modulating_frequencies) if params.logs: @@ -111,7 +203,7 @@ def modulate_samples(p_data_p_samples): max(p_data_p_samples))) if params.plots: plot_helper.samples_fft_plots(p_data_p_samples, "Samples to send", time=True, complex=True, shift=True) - elif params.MODULATION_TYPE == 3: + elif params.MOD == 3: modulated_samples = [] for i in range(len(p_data_p_samples)): modulated_samples.append(fourier_helper.modulate_complex_samples(p_data_p_samples[i], @@ -133,7 +225,6 @@ def scale_samples(p_data_p_modulated_samples): ABS_SAMPLE_RANGE if params.logs: - print("Scaling the signal...") print("Number of samples: {}".format(len(samples_to_send))) print("Minimum sample after scaling: {}".format(min(samples_to_send))) print("Maximum sample after scaling: {}".format(max(samples_to_send))) From aed73857a293d93dac6a8dfed48a179a2c2be494 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Tue, 28 May 2019 23:57:41 +0200 Subject: [PATCH 11/20] Make the receiver work almost for parity check, code awful as hell --- src/main.py | 7 +- src/params.py | 4 +- src/receiver.py | 66 +++------------ src/receiver_helper.py | 172 +++++++++++++++++++++++++++++++++++--- src/transmitter.py | 23 ++--- src/transmitter_helper.py | 19 ++++- 6 files changed, 202 insertions(+), 89 deletions(-) diff --git a/src/main.py b/src/main.py index 696ef6f..f354425 100644 --- a/src/main.py +++ b/src/main.py @@ -3,7 +3,6 @@ import params import pulses -import read_write import receiver import transmitter @@ -18,11 +17,9 @@ params.params_log() # Transmitter - symbols = transmitter.encoder() _, h = pulses.root_raised_cosine() - samples_to_send = transmitter.waveform_former(h, symbols) - read_write.write_samples(samples_to_send) + samples_to_send = transmitter.waveform_former(h, transmitter.encoder()) transmitter.send_samples() # Receiver - received_symbols = receiver.n_tuple_former() + receiver.decoder(receiver.n_tuple_former()) diff --git a/src/params.py b/src/params.py index a177e08..002869e 100644 --- a/src/params.py +++ b/src/params.py @@ -14,7 +14,7 @@ def choose_symbol_period(): # General variables logs = True -plots = True +plots = False input_message_file_path = "../data/input_text.txt" output_message_file_path = "../data/output_text.txt" @@ -45,7 +45,7 @@ def choose_symbol_period(): M = 4 # length of the mapping (must be of the form 2^2k if QAM is chosen) BITS_PER_SYMBOL = int(np.log2(M)) # number of bits we transmit per symbol -MOD = 1 +MOD = 3 # 1 = naive approach (duplicate 4 times) # 2 = less naive approach (duplicate 2 times, (care about covering 4000Hz with the rrc --> choose T accordingly)) # 3 = parity check approach (only works for M=4 here) diff --git a/src/receiver.py b/src/receiver.py index 812f4ff..8fed2d4 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -1,10 +1,7 @@ -import numpy as np import time import sys -import helper import params -import read_write import mappings import receiver_helper @@ -51,59 +48,24 @@ def n_tuple_former(): # Down-sample the samples to obtain the symbols data_symbols = receiver_helper.down_sample(data_samples) - return data_symbols + return data_symbols, removed_freq_range -def decoder(symbols, mapping): +def decoder(symbols, removed_freq_range): """ Map the received symbols to the closest symbols of our mapping + :param removed_freq_range: index of the range that was removed by the server :param symbols: the observation vector, i.e the received symbols - :param mapping: the chosen mapping for the communication :return: integers between 0 and M-1, i.e integers corresponding to the bits sent """ - if params.logs: - print("Decoding the symbols...") - if params.MOD == 1 or params.MOD == 2: - ints = receiver_helper.symbols_to_ints(symbols, mapping) - - if params.logs: - print("Equivalent integers:\n{}".format(ints)) - - # Convert the ints to BITS_PER_SYMBOL bits - bits = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] - if params.logs: - print("Groups of BITS_PER_SYMBOL bits representing each integer:\n{}".format(bits)) - - # Make a new string with it - bits = ''.join(bits) - if params.logs: - print("Bits grouped all together:\n{}".format(bits)) - - # Slice the string into substrings of 7 characters - bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] - if params.logs: - print("Groups of 7 bits:\n{}".format(bits)) - - # Add a zero at the beginning of each substring (cf transmitter) - new_bits = [] - for sub_string in bits: - new_bits.append('0' + sub_string) - if params.logs: - print("Groups of 8 bits (0 added at the beginning, cf. transmitter):\n{}".format(new_bits)) - - # Convert from array of bytes to string - message_received = ''.join(helper.bits2string(new_bits)) - - message_sent = read_write.read_message_sent() - print("Message sent: {}".format(message_sent)) - print("Message received: {}".format(message_received)) - helper.compare_messages(message_sent, message_received) - elif params.MOD == 3: - return None - else: - raise ValueError("This modulation type does not exist yet... He he he") - if params.logs: - print("--------------------------------------------------------") + # Choose the mapping according to the params file + mapping = mappings.choose_mapping() + + # Associate the received symbols to integers (indices of our mapping) + ints = receiver_helper.symbols_to_ints(symbols, mapping) + + # Deduce the received message + message_received = receiver_helper.ints_to_message(ints, removed_freq_range) return message_received @@ -113,7 +75,5 @@ def decoder(symbols, mapping): moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) log_file = open("../logs/" + moment + ".log", "w+") sys.stdout = log_file - - symbols = n_tuple_former() - message = decoder(symbols, mappings.choose_mapping()) - read_write.write_message_received(message) + data_symbols, removed_freq_range = n_tuple_former() + decoder(data_symbols, removed_freq_range) diff --git a/src/receiver_helper.py b/src/receiver_helper.py index 1b95627..21ca625 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -1,11 +1,12 @@ import numpy as np +import fourier_helper +import helper +import parameter_estim import params import plot_helper -import read_write -import fourier_helper import pulses -import parameter_estim +import read_write def prepare_data(): @@ -14,7 +15,7 @@ def prepare_data(): # Load the input and output samples from their respective files input_samples = np.loadtxt(params.input_sample_file_path) # TODO: put output again - samples_received = np.loadtxt(params.output_sample_file_path) + samples_received = np.loadtxt(params.input_sample_file_path) # Plot the input and output samples in Time domain and Frequency domain if params.plots: @@ -105,9 +106,10 @@ def low_pass(demodulated_samples, indices_available): y = [] for i in range(len(demodulated_samples)): y.append(np.convolve(demodulated_samples[i], h_matched)) - for i in range(len(y)): - plot_helper.samples_fft_plots( - y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) + if params.plots: + for i in range(len(y)): + plot_helper.samples_fft_plots( + y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) if params.logs: print("Y shape:") for i in range(len(y)): @@ -315,7 +317,22 @@ def down_sample(data_samples): def symbols_to_ints(symbols, mapping): if params.logs: - print("Associating symbols to integers (indices of our mapping)...") + print("Associating symbols to integers...") + if params.MOD == 1 or params.MOD == 2: + ints = symbols_to_ints_helper(symbols, mapping) + elif params.MOD == 3: + ints = [] + for i in range(len(symbols)): + ints.append(symbols_to_ints_helper(symbols[i], mapping)) + else: + raise ValueError("This modulation type does not exist yet... He he he") + if params.logs: + print("Integers:\n{}".format(ints)) + print("--------------------------------------------------------") + return ints + + +def symbols_to_ints_helper(symbols, mapping): # Make sure symbols and mapping have less or equal than 2 dimensions if len(np.shape(symbols)) > 2 or len(np.shape(mapping)) > 2: raise AttributeError("One of the vectors symbols and mapping has more than 2 dimensions!") @@ -339,8 +356,141 @@ def symbols_to_ints(symbols, mapping): S = len(symbols) distances = np.transpose(abs(np.tile(symbols, (M, 1)) - np.tile(mapping, (1, S)))) - if params.logs: - print("--------------------------------------------------------") - return np.argmin(distances, 1) + ints = np.argmin(distances, 1) + return ints + +def ints_to_message(ints, removed_freq_range): + if params.MOD == 1 or params.MOD == 2: + # Convert the ints to BITS_PER_SYMBOL bits + bits = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] + if params.logs: + print("Groups of BITS_PER_SYMBOL bits representing each integer:\n{}".format(bits)) + # Make a new string with it + bits = ''.join(bits) + if params.logs: + print("Bits grouped all together:\n{}".format(bits)) + + # Slice the string into substrings of 7 characters + bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] + if params.logs: + print("Groups of 7 bits:\n{}".format(bits)) + + # Add a zero at the beginning of each substring (cf transmitter) + new_bits = [] + for sub_string in bits: + new_bits.append('0' + sub_string) + if params.logs: + print("Groups of 8 bits (0 added at the beginning, cf. transmitter):\n{}".format(new_bits)) + elif params.MOD == 3: + print("Removed frequency range: {}".format(removed_freq_range)) + bits_grouped_by_bits_per_symbol = [] + for j in range(len(ints)): + bits_grouped_by_bits_per_symbol.append( + ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints[j]]) + print(bits_grouped_by_bits_per_symbol) + print(np.shape(bits_grouped_by_bits_per_symbol)) + print() + + bits_grouped = [] + for j in range(len(bits_grouped_by_bits_per_symbol)): + # Make a new string with it + bits_grouped.append(''.join(bits_grouped_by_bits_per_symbol[j])) + + print(bits_grouped) + print(np.shape(bits_grouped)) + print() + + bits = [] + for i in range(len(bits_grouped)): + bits_alone = [] + for j in range(len(bits_grouped[i])): + bits_alone.append(int(bits_grouped[i][j])) + bits.append(bits_alone) + + print(bits) + print(np.shape(bits)) + print() + + if removed_freq_range != 3: + sum = np.sum(bits, axis=0) + + print(sum) + print(np.shape(sum)) + print() + + reconstructed_bit_stream = [] + for j in range(len(sum)): + reconstructed_bit_stream.append(0 if sum[j] % 2 == 0 else 1) + + print(reconstructed_bit_stream) + print(np.shape(reconstructed_bit_stream)) + print() + + bit_streams = np.zeros((len(params.FREQ_RANGES) - 1, len(reconstructed_bit_stream))) + + booleans = [] + for i in range(len(params.FREQ_RANGES)): + booleans.append(True if i != removed_freq_range else False) + + for i in range(len(bit_streams)): + if booleans[i]: + bit_streams[i] = bits[i] + else: + bit_streams[i] = reconstructed_bit_stream + else: + bit_streams = bits + + print(bit_streams) + print(np.shape(bit_streams)) + print() + + bits = [] + for i in range(len(bit_streams[0])): + for j in range(len(bit_streams)): + bits.append(int(bit_streams[j][i])) + + print("Bits:") + print(bits) + print(np.shape(bits)) + print() + + # Slice the string into substrings of 7 characters + bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] + + print("Bits in groups of 7") + print(bits) + print(np.shape(bits)) + print() + + if len(bits[len(bits) - 1]) != 7: + bits = bits[:len(bits) - 1] + + strings = [] + for i in range(len(bits)): + strings.append(''.join(str(x) for x in bits[i])) + + new_bits = [] + for sub_string in strings: + new_bits.append('0' + str(sub_string)) + + print(new_bits) + print(np.shape(new_bits)) + print() + else: + raise ValueError("This modulation type does not exist yet... He he he") + + # Convert from array of bytes to string + message_received = ''.join(helper.bits2string(new_bits)) + + message_sent = read_write.read_message_sent() + if params.logs: + print("Message sent: {}".format(message_sent)) + print("Message received: {}".format(message_received)) + if params.logs: + helper.compare_messages(message_sent, message_received) + read_write.write_message_received(message_received) + if params.logs: + print("--------------------------------------------------------") + return message_received diff --git a/src/transmitter.py b/src/transmitter.py index 8c74cec..045410c 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -1,4 +1,3 @@ -import subprocess import time import sys import numpy as np @@ -49,21 +48,14 @@ def waveform_former(h, data_symbols, USF=params.USF): # Scale the signal to the range [-1, 1] (with a bit of uncertainty margin, according to params.ABS_SAMPLE_RANGE) samples_to_send = transmitter_helper.scale_samples(p_data_p_modulated_samples) + # Write the samples to send in the appropriate file + read_write.write_samples(samples_to_send) + return samples_to_send def send_samples(): - """ - Launch the client.py file with the correct arguments according to the parameters in the param file - :return: None - """ - subprocess.call(["python3 client.py" + - " --input_file=" + params.input_sample_file_path + - " --output_file=" + params.output_sample_file_path + - " --srv_hostname=" + params.server_hostname + - " --srv_port=" + str(params.server_port)], - shell=True) - return None + transmitter_helper.send_samples() # Intended for testing (to run the program, run main.py) @@ -81,11 +73,8 @@ def send_samples(): _, h_pulse = pulses.root_raised_cosine() # Construct the samples to send - samples = waveform_former(h_pulse, symbols) - - # Write the samples in the input file - read_write.write_samples(samples) + waveform_former(h_pulse, symbols) # Send the samples to the server - send_samples() + transmitter_helper.send_samples() diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 9f84d10..f0752e6 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -1,5 +1,6 @@ import numpy as np from scipy.signal import upfirdn +import subprocess import params import read_write @@ -193,7 +194,6 @@ def modulate_samples(p_data_p_samples): else: raise ValueError("This mapping type does not exist yet... He he he") - # Modulate the samples to fit in the required bands if np.any(np.iscomplex(p_data_p_samples)): if params.MOD == 1 or params.MOD == 2: p_data_p_modulated_samples = fourier_helper.modulate_complex_samples(p_data_p_samples, @@ -230,3 +230,20 @@ def scale_samples(p_data_p_modulated_samples): print("Maximum sample after scaling: {}".format(max(samples_to_send))) print("--------------------------------------------------------") return samples_to_send + + +def send_samples(): + """ + Launch the client.py file with the correct arguments according to the parameters in the param file + :return: None + """ + subprocess.call(["python3 client.py" + + " --input_file=" + params.input_sample_file_path + + " --output_file=" + params.output_sample_file_path + + " --srv_hostname=" + params.server_hostname + + " --srv_port=" + str(params.server_port)], + shell=True) + if params.logs: + print("Samples sent!") + print("--------------------------------------------------------") + return None From c4f444d61d2812bb37c2e84620d11aa6845c61d5 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Wed, 29 May 2019 14:11:39 +0200 Subject: [PATCH 12/20] Parity check works <3 --- src/helper.py | 2 +- src/main.py | 3 +- src/receiver_helper.py | 102 ++++++++++++++++++-------------------- src/transmitter_helper.py | 10 ++-- 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/src/helper.py b/src/helper.py index 7a4cf57..52067f0 100644 --- a/src/helper.py +++ b/src/helper.py @@ -29,7 +29,7 @@ def compare_messages(message_sent, message): comparison += " " else: n_errors += 1 - comparison += '-' + comparison += '!' print("Errors: {}".format(comparison)) if not same_length: diff --git a/src/main.py b/src/main.py index f354425..150d8d0 100644 --- a/src/main.py +++ b/src/main.py @@ -22,4 +22,5 @@ transmitter.send_samples() # Receiver - receiver.decoder(receiver.n_tuple_former()) + data_symbols, removed_freq_range = receiver.n_tuple_former() + receiver.decoder(data_symbols, removed_freq_range) \ No newline at end of file diff --git a/src/receiver_helper.py b/src/receiver_helper.py index 21ca625..89b5317 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -14,8 +14,7 @@ def prepare_data(): print("Preparing the data...") # Load the input and output samples from their respective files input_samples = np.loadtxt(params.input_sample_file_path) - # TODO: put output again - samples_received = np.loadtxt(params.input_sample_file_path) + samples_received = np.loadtxt(params.output_sample_file_path) # Plot the input and output samples in Time domain and Frequency domain if params.plots: @@ -363,113 +362,106 @@ def symbols_to_ints_helper(symbols, mapping): def ints_to_message(ints, removed_freq_range): if params.MOD == 1 or params.MOD == 2: # Convert the ints to BITS_PER_SYMBOL bits - bits = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] + bits_separated = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] if params.logs: - print("Groups of BITS_PER_SYMBOL bits representing each integer:\n{}".format(bits)) + print("Groups of BITS_PER_SYMBOL bits representing each integer:\n{}".format(bits_separated)) # Make a new string with it - bits = ''.join(bits) + bits_separated = ''.join(bits_separated) if params.logs: - print("Bits grouped all together:\n{}".format(bits)) + print("Bits grouped all together:\n{}".format(bits_separated)) # Slice the string into substrings of 7 characters - bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] + bits_separated = [bits_separated[i:i + 7] for i in range(0, len(bits_separated), 7)] if params.logs: - print("Groups of 7 bits:\n{}".format(bits)) + print("Groups of 7 bits:\n{}".format(bits_separated)) # Add a zero at the beginning of each substring (cf transmitter) new_bits = [] - for sub_string in bits: + for sub_string in bits_separated: new_bits.append('0' + sub_string) if params.logs: print("Groups of 8 bits (0 added at the beginning, cf. transmitter):\n{}".format(new_bits)) + elif params.MOD == 3: - print("Removed frequency range: {}".format(removed_freq_range)) bits_grouped_by_bits_per_symbol = [] for j in range(len(ints)): bits_grouped_by_bits_per_symbol.append( ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints[j]]) - print(bits_grouped_by_bits_per_symbol) - print(np.shape(bits_grouped_by_bits_per_symbol)) - print() + print("Bits grouped by groups of BITS_PER_SYMBOL bits: ({})\n{}\n".format + (np.shape(bits_grouped_by_bits_per_symbol), bits_grouped_by_bits_per_symbol)) + # Make an array of strings with it bits_grouped = [] for j in range(len(bits_grouped_by_bits_per_symbol)): - # Make a new string with it bits_grouped.append(''.join(bits_grouped_by_bits_per_symbol[j])) - print(bits_grouped) - print(np.shape(bits_grouped)) + print("Bits grouped: ({})".format(np.shape(bits_grouped))) + for i in range(len(bits_grouped)): + print(bits_grouped[i]) print() - bits = [] + # Separate bits + bits_separated = [] for i in range(len(bits_grouped)): bits_alone = [] for j in range(len(bits_grouped[i])): bits_alone.append(int(bits_grouped[i][j])) - bits.append(bits_alone) + bits_separated.append(bits_alone) - print(bits) - print(np.shape(bits)) + print("Bits separated: ({})".format(np.shape(bits_separated))) + for i in range(len(bits_separated)): + print(bits_separated[i]) print() + print("Removed frequency range: {}".format(removed_freq_range)) if removed_freq_range != 3: - sum = np.sum(bits, axis=0) - - print(sum) - print(np.shape(sum)) - print() + print("--> We have to reconstruct the missing bit stream") + parity_check = np.sum(bits_separated, axis=0) reconstructed_bit_stream = [] - for j in range(len(sum)): - reconstructed_bit_stream.append(0 if sum[j] % 2 == 0 else 1) + for j in range(len(parity_check)): + reconstructed_bit_stream.append(0 if parity_check[j] % 2 == 0 else 1) + print("Missing bit stream: ({})".format(np.shape(reconstructed_bit_stream))) print(reconstructed_bit_stream) - print(np.shape(reconstructed_bit_stream)) print() - bit_streams = np.zeros((len(params.FREQ_RANGES) - 1, len(reconstructed_bit_stream))) - - booleans = [] - for i in range(len(params.FREQ_RANGES)): - booleans.append(True if i != removed_freq_range else False) - - for i in range(len(bit_streams)): - if booleans[i]: - bit_streams[i] = bits[i] - else: - bit_streams[i] = reconstructed_bit_stream + bit_streams_to_use = bits_separated[:len(bits_separated) - 1] + bit_streams_to_use.insert(removed_freq_range, reconstructed_bit_stream) else: - bit_streams = bits + print("--> We can use the received bit streams") + bit_streams_to_use = bits_separated - print(bit_streams) - print(np.shape(bit_streams)) + print("Bit streams to use: ({})".format(np.shape(bits_separated))) + for i in range(len(bit_streams_to_use)): + print(bit_streams_to_use[i]) print() - bits = [] - for i in range(len(bit_streams[0])): - for j in range(len(bit_streams)): - bits.append(int(bit_streams[j][i])) + bits_separated = [] + for i in range(len(bit_streams_to_use[0])): + for j in range(len(bit_streams_to_use)): + bits_separated.append(int(bit_streams_to_use[j][i])) print("Bits:") - print(bits) - print(np.shape(bits)) + print(bits_separated) + print(np.shape(bits_separated)) print() # Slice the string into substrings of 7 characters - bits = [bits[i:i + 7] for i in range(0, len(bits), 7)] + bits_separated = [bits_separated[i:i + 7] for i in range(0, len(bits_separated), 7)] print("Bits in groups of 7") - print(bits) - print(np.shape(bits)) + print(bits_separated) + print(np.shape(bits_separated)) print() - if len(bits[len(bits) - 1]) != 7: - bits = bits[:len(bits) - 1] + if len(bits_separated[len(bits_separated) - 1]) != 7: + bits_separated = bits_separated[:len(bits_separated) - 1] strings = [] - for i in range(len(bits)): - strings.append(''.join(str(x) for x in bits[i])) + for i in range(len(bits_separated)): + strings.append(''.join(str(x) for x in bits_separated[i])) new_bits = [] for sub_string in strings: diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index f0752e6..13f02eb 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -58,7 +58,7 @@ def message_bytes_to_int(new_bits): # Fill the bit streams arrays for i in range(len(new_bits)): - bit_streams[i % (n_bit_streams - 1)][int(np.ceil(i / (n_bit_streams - 1)))] = new_bits[i] + bit_streams[i % (n_bit_streams - 1)][int(np.floor(i / (n_bit_streams - 1)))] = new_bits[i] # Construct the parity check bit stream and insert it in the bit streams array pc_bit_stream = np.sum(bit_streams[:n_bit_streams - 1], axis=0) @@ -67,15 +67,17 @@ def message_bytes_to_int(new_bits): bit_streams[n_bit_streams - 1] = pc_bit_stream if params.logs: - print(" ") - print("Bit stream {}:\n{}".format(bit_streams.shape, bit_streams)) + print("Bit streams: {}".format(np.shape(bit_streams))) + for i in range(len(bit_streams)): + print("{}".format(bit_streams[i])) print("--------------------------------------------------------") # Group them by groups of BITS_PER_SYMBOL bits ints = np.zeros((n_bit_streams, int(len_bit_streams / 2)), dtype=str) for i in range(n_bit_streams): for j in range(int(len_bit_streams / params.BITS_PER_SYMBOL)): - grouped_bits = str(bit_streams[i][j]) + str(bit_streams[i][j + params.BITS_PER_SYMBOL - 1]) + index = j*params.BITS_PER_SYMBOL + grouped_bits = ''.join(map(str, bit_streams[i][index:index + params.BITS_PER_SYMBOL])) mapping_index = int(grouped_bits, base=2) ints[i][j] = mapping_index From 78e7a7db4c869c6db705759f96a1cf60750c0411 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Wed, 29 May 2019 15:16:17 +0200 Subject: [PATCH 13/20] Start to comment and clean the code --- src/main.py | 2 +- src/mappings.py | 46 +++++++++++++------------- src/params.py | 2 +- src/receiver_helper.py | 68 ++++++++++++++++++--------------------- src/transmitter_helper.py | 6 ++-- 5 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/main.py b/src/main.py index 150d8d0..6b2e54c 100644 --- a/src/main.py +++ b/src/main.py @@ -23,4 +23,4 @@ # Receiver data_symbols, removed_freq_range = receiver.n_tuple_former() - receiver.decoder(data_symbols, removed_freq_range) \ No newline at end of file + receiver.decoder(data_symbols, removed_freq_range) diff --git a/src/mappings.py b/src/mappings.py index a81b243..f3cad66 100644 --- a/src/mappings.py +++ b/src/mappings.py @@ -11,33 +11,35 @@ def qam_map(M): """ log_sqrt_m = np.log2(np.sqrt(M)) if log_sqrt_m != np.ceil(log_sqrt_m): - raise ValueError('Parameter[M] is not of the form 2^2K, K a positive integer.') + raise ValueError("Parameter[M] is not of the form 2^2K, K a positive integer.") # Implement Gray code if M == 4: return [1 + 1j, 1 - 1j, -1 + 1j, -1 - 1j] elif M == 16: - raise ValueError("TODO: implement gray code for M=16") + return [-3-3j, -3-1j, -3+3j, -3+1j, -1-3j, -1-1j, -1+3j, -1+1j, 3-3j, 3-1j, 3+3j, 3+1j, 1-3j, 1-1j, 1+3j, 1+1j] else: - N = np.sqrt(M) - 1 - aux = np.arange(-N, N + 1, 2) - x, y = np.meshgrid(aux[::-1], aux[::-1]) - a = (x + y * 1j).T - size_a = len(a) ** 2 - b = np.zeros(size_a, dtype=complex) - c = 0 - i = 0 - j = 0 - while c < size_a: - b[c] = a[j][i] - c += 1 - if (i == len(a) - 1 and j % 2 == 0) or (i == 0 and j % 2 == 1): - j += 1 - continue - if j % 2 == 1: - i -= 1 - else: - i += 1 - return b + raise ValueError("This mapping does not exist yet... He he he") + # N = np.sqrt(M) - 1 + # aux = np.arange(-N, N + 1, 2) + # x, y = np.meshgrid(aux[::-1], aux[::-1]) + # a = (x + y * 1j).T + # size_a = len(a) ** 2 + # b = np.zeros(size_a, dtype=complex) + # c = 0 + # i = 0 + # j = 0 + # while c < size_a: + # b[c] = a[j][i] + # c += 1 + # if (i == len(a) - 1 and j % 2 == 0) or (i == 0 and j % 2 == 1): + # j += 1 + # continue + # if j % 2 == 1: + # i -= 1 + # else: + # i += 1 + # return b + def psk_map(M): diff --git a/src/params.py b/src/params.py index 002869e..8315dcd 100644 --- a/src/params.py +++ b/src/params.py @@ -14,7 +14,7 @@ def choose_symbol_period(): # General variables logs = True -plots = False +plots = True input_message_file_path = "../data/input_text.txt" output_message_file_path = "../data/output_text.txt" diff --git a/src/receiver_helper.py b/src/receiver_helper.py index 89b5317..df4ecba 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -395,11 +395,11 @@ def ints_to_message(ints, removed_freq_range): bits_grouped = [] for j in range(len(bits_grouped_by_bits_per_symbol)): bits_grouped.append(''.join(bits_grouped_by_bits_per_symbol[j])) - - print("Bits grouped: ({})".format(np.shape(bits_grouped))) - for i in range(len(bits_grouped)): - print(bits_grouped[i]) - print() + if params.logs: + print("Bits grouped: ({})".format(np.shape(bits_grouped))) + for i in range(len(bits_grouped)): + print(bits_grouped[i]) + print() # Separate bits bits_separated = [] @@ -408,68 +408,64 @@ def ints_to_message(ints, removed_freq_range): for j in range(len(bits_grouped[i])): bits_alone.append(int(bits_grouped[i][j])) bits_separated.append(bits_alone) + if params.logs: + print("Bits separated: {}".format(np.shape(bits_separated))) + for i in range(len(bits_separated)): + print(bits_separated[i]) + print() - print("Bits separated: ({})".format(np.shape(bits_separated))) - for i in range(len(bits_separated)): - print(bits_separated[i]) - print() - - print("Removed frequency range: {}".format(removed_freq_range)) + if params.logs: + print("Removed frequency range: {}".format(removed_freq_range)) if removed_freq_range != 3: - print("--> We have to reconstruct the missing bit stream") + if params.logs: + print("--> We have to reconstruct the missing bit stream") parity_check = np.sum(bits_separated, axis=0) reconstructed_bit_stream = [] for j in range(len(parity_check)): reconstructed_bit_stream.append(0 if parity_check[j] % 2 == 0 else 1) - - print("Missing bit stream: ({})".format(np.shape(reconstructed_bit_stream))) - print(reconstructed_bit_stream) - print() + if params.logs: + print("Missing bit stream: {}\n{}\n".format(np.shape(reconstructed_bit_stream), reconstructed_bit_stream)) bit_streams_to_use = bits_separated[:len(bits_separated) - 1] bit_streams_to_use.insert(removed_freq_range, reconstructed_bit_stream) else: - print("--> We can use the received bit streams") + if params.logs: + print("--> We can use the received bit streams") bit_streams_to_use = bits_separated - print("Bit streams to use: ({})".format(np.shape(bits_separated))) - for i in range(len(bit_streams_to_use)): - print(bit_streams_to_use[i]) - print() + if params.logs: + print("Bit streams to use: {}".format(np.shape(bits_separated))) + for i in range(len(bit_streams_to_use)): + print(bit_streams_to_use[i]) + print() + # Isolate the bits bits_separated = [] for i in range(len(bit_streams_to_use[0])): for j in range(len(bit_streams_to_use)): bits_separated.append(int(bit_streams_to_use[j][i])) - - print("Bits:") - print(bits_separated) - print(np.shape(bits_separated)) - print() + if params.logs: + print("Bits in order: {}\n{}\n".format(np.shape(np.shape(bits_separated)), bits_separated)) # Slice the string into substrings of 7 characters bits_separated = [bits_separated[i:i + 7] for i in range(0, len(bits_separated), 7)] + if params.logs: + print("Bits in groups of 7: {}\n{}\n".format(np.shape(bits_separated), bits_separated)) - print("Bits in groups of 7") - print(bits_separated) - print(np.shape(bits_separated)) - print() - + # Remove the last element if it is not composed of 7 bits (comes from the rounding) if len(bits_separated[len(bits_separated) - 1]) != 7: bits_separated = bits_separated[:len(bits_separated) - 1] + # Re-put the zero at the beginning (c.f transmitter) strings = [] for i in range(len(bits_separated)): strings.append(''.join(str(x) for x in bits_separated[i])) - new_bits = [] for sub_string in strings: new_bits.append('0' + str(sub_string)) - - print(new_bits) - print(np.shape(new_bits)) - print() + if params.logs: + print("Final bytes: {}\n{}\n".format(np.shape(new_bits), new_bits)) else: raise ValueError("This modulation type does not exist yet... He he he") diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 13f02eb..cd8b84a 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -50,8 +50,8 @@ def message_bytes_to_int(new_bits): len_bit_streams = int(np.ceil(len(new_bits) / (n_bit_streams - 1))) # Make it even - if len_bit_streams % 2 != 0: - len_bit_streams = len_bit_streams + 1 + while len_bit_streams % params.BITS_PER_SYMBOL != 0: + len_bit_streams += 1 # Construct the bit streams array with zeros bit_streams = np.zeros((n_bit_streams, len_bit_streams), dtype=int) @@ -73,7 +73,7 @@ def message_bytes_to_int(new_bits): print("--------------------------------------------------------") # Group them by groups of BITS_PER_SYMBOL bits - ints = np.zeros((n_bit_streams, int(len_bit_streams / 2)), dtype=str) + ints = np.zeros((n_bit_streams, int(len_bit_streams / params.BITS_PER_SYMBOL)), dtype=str) for i in range(n_bit_streams): for j in range(int(len_bit_streams / params.BITS_PER_SYMBOL)): index = j*params.BITS_PER_SYMBOL From 894271e19499be84181334742eedc20c19b0dc07 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Wed, 29 May 2019 16:58:05 +0200 Subject: [PATCH 14/20] Find good parameters --- data/input_text.txt | 2 +- src/params.py | 2 +- src/pulses.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/input_text.txt b/data/input_text.txt index 7362506..2f44fe4 100644 --- a/data/input_text.txt +++ b/data/input_text.txt @@ -1 +1 @@ -A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was. \ No newline at end of file +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient \ No newline at end of file diff --git a/src/params.py b/src/params.py index 8315dcd..c7d289b 100644 --- a/src/params.py +++ b/src/params.py @@ -53,7 +53,7 @@ def choose_symbol_period(): MOD_2_BANDWIDTH = 4000 MOD_3_BANDWIDTH = 2000 -BETA = 0.2 # rolloff factor of our root-raised-cosine pulse (usually between 0.2 and 0.3 (said Prandoni)) +BETA = 0.05 # rolloff factor of our root-raised-cosine pulse (usually between 0.2 and 0.3 (said Prandoni)) T = choose_symbol_period() # symbol period (in seconds), i.e time before we can repeat the pulse while satisfying # Nyquist crit. NORMALIZE_PULSE = True # rather we normalize the pulse or not diff --git a/src/pulses.py b/src/pulses.py index 97746de..0013256 100644 --- a/src/pulses.py +++ b/src/pulses.py @@ -60,7 +60,7 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params if params.logs: print("Root-raised-cosine:\nSPAN = {} samples, beta = {}, T = {} seconds, Fs = {} samples per second (Hz)" .format(SPAN, beta, T, Fs)) - print("Index where t = 0: {} ({} values left and {} values right)".format(index_0, index_0, index_0 - 1)) + # print("Index where t = 0: {} ({} values left and {} values right)".format(index_0, index_0, index_0 - 1)) print("Normalized = {}".format(params.NORMALIZE_PULSE)) print("--------------------------------------------------------") if params.plots: From c98935da216719151c6ceb92ca85506a49c78fd4 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Wed, 29 May 2019 21:29:04 +0200 Subject: [PATCH 15/20] Comment the transmitter helper, add logs, stuff --- data/input_text.txt | 2 +- src/params.py | 4 +- src/receiver.py | 2 +- src/receiver_helper.py | 10 ++--- src/transmitter.py | 11 +++-- src/transmitter_helper.py | 84 ++++++++++++++++++++++++++++++--------- 6 files changed, 79 insertions(+), 34 deletions(-) diff --git a/data/input_text.txt b/data/input_text.txt index 2f44fe4..bc56e54 100644 --- a/data/input_text.txt +++ b/data/input_text.txt @@ -1 +1 @@ -Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient \ No newline at end of file +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient. \ No newline at end of file diff --git a/src/params.py b/src/params.py index c7d289b..6af56d8 100644 --- a/src/params.py +++ b/src/params.py @@ -14,7 +14,7 @@ def choose_symbol_period(): # General variables logs = True -plots = True +plots = False input_message_file_path = "../data/input_text.txt" output_message_file_path = "../data/output_text.txt" @@ -53,7 +53,7 @@ def choose_symbol_period(): MOD_2_BANDWIDTH = 4000 MOD_3_BANDWIDTH = 2000 -BETA = 0.05 # rolloff factor of our root-raised-cosine pulse (usually between 0.2 and 0.3 (said Prandoni)) +BETA = 0.15 # rolloff factor of our root-raised-cosine pulse (usually between 0.2 and 0.3 (said Prandoni)) T = choose_symbol_period() # symbol period (in seconds), i.e time before we can repeat the pulse while satisfying # Nyquist crit. NORMALIZE_PULSE = True # rather we normalize the pulse or not diff --git a/src/receiver.py b/src/receiver.py index 8fed2d4..7102056 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -46,7 +46,7 @@ def n_tuple_former(): data_samples = receiver_helper.correct_params(data_samples, phase_shift_estim) # Down-sample the samples to obtain the symbols - data_symbols = receiver_helper.down_sample(data_samples) + data_symbols = receiver_helper.downsample(data_samples) return data_symbols, removed_freq_range diff --git a/src/receiver_helper.py b/src/receiver_helper.py index df4ecba..a3eb242 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -42,8 +42,8 @@ def find_removed_frequency(samples_received): if frequency_ranges_available[i]: indices_available.append(i) if params.logs: - print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) - print("Available indices array: {}".format(indices_available)) + # print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) + # print("Available indices array: {}".format(indices_available)) print("--------------------------------------------------------") return removed_freq_range, frequency_ranges_available, indices_available @@ -287,7 +287,7 @@ def correct_params(data_samples, phase_shift_estim): return data_samples -def down_sample(data_samples): +def downsample(data_samples): if params.logs: print("Down-sampling...") if params.MOD == 1 or params.MOD == 2: @@ -316,7 +316,7 @@ def down_sample(data_samples): def symbols_to_ints(symbols, mapping): if params.logs: - print("Associating symbols to integers...") + print("Mapping symbols to integers...") if params.MOD == 1 or params.MOD == 2: ints = symbols_to_ints_helper(symbols, mapping) elif params.MOD == 3: @@ -431,7 +431,7 @@ def ints_to_message(ints, removed_freq_range): bit_streams_to_use.insert(removed_freq_range, reconstructed_bit_stream) else: if params.logs: - print("--> We can use the received bit streams") + print("--> We can use the received bit streams\n") bit_streams_to_use = bits_separated if params.logs: diff --git a/src/transmitter.py b/src/transmitter.py index 045410c..e994548 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -14,33 +14,32 @@ def encoder(): Encode a message into a sequence of symbols according to the given mapping :return: the corresponding symbols for the message """ - # Retrieve the message from file + # Retrieve the message from the file as bytes message_bytes = transmitter_helper.retrieve_message_as_bytes() # Associate the message bytes to the corresponding symbols - corresponding_symbols = transmitter_helper.message_bytes_to_int(message_bytes) + corresponding_symbols = transmitter_helper.grouped_bytes_to_symbols(message_bytes) return np.asarray(corresponding_symbols) -def waveform_former(h, data_symbols, USF=params.USF): +def waveform_former(h, data_symbols): """ :param h: the sampled pulse :param data_symbols: the data symbols modulating the pulse - :param USF: the up-sampling factor, i.e the number of samples per symbols, also called SPS :return: the samples of a modulated pulse train to send to the server """ # Generate the preamble_symbols and write them in the appropriate file preamble_symbols = transmitter_helper.generate_preamble_to_transmit(len(data_symbols)) # Shape the preamble symbols and write the preamble samples in the preamble_samples file - transmitter_helper.shape_preamble_samples(h, preamble_symbols, USF) + transmitter_helper.shape_preamble_samples(h, preamble_symbols) # Concatenate the data symbols with the preamble symbols at the beginning and at the end p_data_p_symbols = transmitter_helper.concatenate_symbols(preamble_symbols, data_symbols) # Shape each of the symbols array - p_data_p_samples = transmitter_helper.shape_symbols(h, p_data_p_symbols, USF) + p_data_p_samples = transmitter_helper.shape_symbols(h, p_data_p_symbols) # Choose the modulation frequencies and modulate the samples p_data_p_modulated_samples = transmitter_helper.modulate_samples(p_data_p_samples) diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index cd8b84a..50c3669 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -12,6 +12,11 @@ def retrieve_message_as_bytes(): + """ + Retrieve the string message in the appropriate file given by the params file, and return the bits corresponding to + the message as an array of 0 and 1 + :return: The bits corresponding to the message as an array of 0 and 1 + """ if params.logs: print("Retrieving the message from the appropriate file...") @@ -30,24 +35,30 @@ def retrieve_message_as_bytes(): return new_message_bytes_grouped -def message_bytes_to_int(new_bits): +def grouped_bytes_to_symbols(grouped_bytes): + """ + Associate the message bits to an array of corresponding symbols that depend on the chosen mapping + :param grouped_bytes: The bits corresponding to the message as an array of 0 and 1 + :return: The corresponding symbols to the given message as an array + """ + if params.logs: + print("Mapping message bytes to the symbols from our mapping...") if params.MOD == 1 or params.MOD == 2: # New structure with bits_per_symbol bits by row - new_bits = [new_bits[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(new_bits), params.BITS_PER_SYMBOL)] + grouped_bytes = [grouped_bytes[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(grouped_bytes), params.BITS_PER_SYMBOL)] # Convert this new bits sequence to an integer sequence - ints = [[int(b, 2) for b in new_bits]] + ints = [[int(b, 2) for b in grouped_bytes]] if params.logs: - print("Cropped and re-structured bits:\n{}".format(new_bits)) + print("Cropped and re-structured bits:\n{}".format(grouped_bytes)) print("Equivalent integers (indices for our mapping):\n{}".format(ints)) - print("--------------------------------------------------------") elif params.MOD == 3: # Choose the number of bit streams (depends on the number of frequency ranges) n_bit_streams = len(params.FREQ_RANGES) # Choose the length of our bit streams - len_bit_streams = int(np.ceil(len(new_bits) / (n_bit_streams - 1))) + len_bit_streams = int(np.ceil(len(grouped_bytes) / (n_bit_streams - 1))) # Make it even while len_bit_streams % params.BITS_PER_SYMBOL != 0: @@ -57,8 +68,8 @@ def message_bytes_to_int(new_bits): bit_streams = np.zeros((n_bit_streams, len_bit_streams), dtype=int) # Fill the bit streams arrays - for i in range(len(new_bits)): - bit_streams[i % (n_bit_streams - 1)][int(np.floor(i / (n_bit_streams - 1)))] = new_bits[i] + for i in range(len(grouped_bytes)): + bit_streams[i % (n_bit_streams - 1)][int(np.floor(i / (n_bit_streams - 1)))] = grouped_bytes[i] # Construct the parity check bit stream and insert it in the bit streams array pc_bit_stream = np.sum(bit_streams[:n_bit_streams - 1], axis=0) @@ -70,7 +81,6 @@ def message_bytes_to_int(new_bits): print("Bit streams: {}".format(np.shape(bit_streams))) for i in range(len(bit_streams)): print("{}".format(bit_streams[i])) - print("--------------------------------------------------------") # Group them by groups of BITS_PER_SYMBOL bits ints = np.zeros((n_bit_streams, int(len_bit_streams / params.BITS_PER_SYMBOL)), dtype=str) @@ -83,7 +93,6 @@ def message_bytes_to_int(new_bits): if params.logs: print("Ints bits stream {}:\n{}".format(ints.shape, ints)) - print("--------------------------------------------------------") else: raise ValueError("This modulation type does not exist yet... He he he") @@ -93,17 +102,22 @@ def message_bytes_to_int(new_bits): corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] if params.logs: - print("Mapping the integers to the symbols in the mapping...") print("Symbols/n-tuples to be sent:\n{}".format(corresponding_symbols)) print("Shape of the symbols: {}".format(np.shape(corresponding_symbols))) - print("--------------------------------------------------------") if params.plots: plot_helper.plot_complex_symbols(corresponding_symbols, "{} data symbols to send" .format(np.shape(corresponding_symbols)), "blue") + if params.logs: + print("--------------------------------------------------------") return corresponding_symbols def generate_preamble_to_transmit(len_data_symbols): + """ + Generate preamble symbols that will be used to synchronize our signal at the receiver + :param len_data_symbols: The number of symbols to transmit (useful in case of a random preamble) + :return: The preamble symbols that were generated + """ if params.logs: print("Generating the preamble...") @@ -119,6 +133,13 @@ def generate_preamble_to_transmit(len_data_symbols): def concatenate_symbols(preamble_symbols, data_symbols): + """ + Construct the symbols to send by concatenating the data symbols with the preamble symbols at the beginning and at + th end + :param preamble_symbols: The preamble symbols to stick to the data symbols + :param data_symbols: The symbols that carry the data to send + :return: The symbols that result from the concatenation + """ if params.logs: print("Concatenating everything together (preamble-data-flipped preamble)...") @@ -133,8 +154,8 @@ def concatenate_symbols(preamble_symbols, data_symbols): p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) if params.logs: for i in range(len(p_data_p_symbols)): - print("Total symbols {}: {}".format(i, p_data_p_symbols)) - print("Number of total symbols {}: {}".format(i, np.shape(p_data_p_symbols))) + print("Total symbols {}: {}".format(i, p_data_p_symbols[i])) + print("Number of total symbols {}: {}".format(i, np.shape(p_data_p_symbols[i]))) if params.plots: plot_helper.plot_complex_symbols(p_data_p_symbols[i], "Symbols {}".format(i)) else: @@ -142,12 +163,18 @@ def concatenate_symbols(preamble_symbols, data_symbols): return p_data_p_symbols -def shape_symbols(h, p_data_p_symbols, USF): +def shape_symbols(h, p_data_p_symbols): + """ + Use the pulse h to shape the p_data_p_symbols given, and up-sample by USF before + :param h: The pulse to use to do the pulse shaping + :param p_data_p_symbols: The preamble-data-preamble symbols to shape + :return: The preamble-data-preamble sample resulting from the pulse shaping + """ if params.logs: print("Pulse shaping the symbols...") if params.MOD == 1 or params.MOD == 2: - p_data_p_samples = upfirdn(h, p_data_p_symbols, USF) + p_data_p_samples = upfirdn(h, p_data_p_symbols, params.USF) if params.logs: print("Samples: {}".format(p_data_p_samples)) print("Up-sampling factor: {}".format(params.USF)) @@ -157,7 +184,7 @@ def shape_symbols(h, p_data_p_symbols, USF): elif params.MOD == 3: p_data_p_samples = [] for i in range(len(p_data_p_symbols)): - p_data_p_samples.append(upfirdn(h, p_data_p_symbols[i], USF)) + p_data_p_samples.append(upfirdn(h, p_data_p_symbols[i], params.USF)) if params.plots: for i in range(len(p_data_p_samples)): plot_helper.samples_fft_plots(p_data_p_samples[i], "Samples {} after the pulse shaping".format(i), @@ -170,11 +197,17 @@ def shape_symbols(h, p_data_p_symbols, USF): return p_data_p_samples -def shape_preamble_samples(h, preamble_symbols, USF): +def shape_preamble_samples(h, preamble_symbols): + """ + Use the pulse h to shape the preamble_symbols given, and up-sample by USF before + :param h: The pulse to use to do the pulse shaping + :param preamble_symbols: The preamble symbols to shape + :return: The preamble sample resulting from the pulse shaping + """ if params.logs: print("Shaping the preamble...") - preamble_samples = upfirdn(h, preamble_symbols, USF) + preamble_samples = upfirdn(h, preamble_symbols, params.USF) read_write.write_preamble_samples(preamble_samples) if params.logs: @@ -187,6 +220,11 @@ def shape_preamble_samples(h, preamble_symbols, USF): def modulate_samples(p_data_p_samples): + """ + Modulate the samples to the allowed frequency ranges + :param p_data_p_samples: The samples to modulate + :return: The modulated samples + """ if params.logs: print("Choosing the modulation frequencies and modulating the samples...") if params.MOD == 1 or params.MOD == 3: @@ -221,6 +259,11 @@ def modulate_samples(p_data_p_samples): def scale_samples(p_data_p_modulated_samples): + """ + Scale the samples to fit to the server's constraints + :param p_data_p_modulated_samples: The samples to scale + :return: The samples scaled + """ if params.logs: print("Scaling the samples to the server constraints...") samples_to_send = p_data_p_modulated_samples / (np.max(np.abs(p_data_p_modulated_samples))) * params.\ @@ -246,6 +289,9 @@ def send_samples(): " --srv_port=" + str(params.server_port)], shell=True) if params.logs: + print("--------------------------------------------------------") + print("--------------------------------------------------------") print("Samples sent!") print("--------------------------------------------------------") + print("--------------------------------------------------------") return None From 77b66f36b7d03f16c84053b8f699190b3f4dbe08 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Thu, 30 May 2019 16:52:07 +0200 Subject: [PATCH 16/20] Comment and refactor the code --- data/input_text.txt | 2 +- src/fourier_helper.py | 35 ++++---- src/helper.py | 23 ++--- src/local_test.py | 8 +- src/mappings.py | 48 ++++------ src/parameter_estim.py | 13 +-- src/params.py | 6 ++ src/plot_helper.py | 115 +++++++++++++++--------- src/preambles.py | 19 +++- src/pulses.py | 24 +++-- src/read_write.py | 50 ++++++----- src/receiver.py | 33 +++---- src/receiver_helper.py | 178 ++++++++++++++++++++++++++++++++------ src/scrambler.py | 2 + src/transmitter.py | 41 +++------ src/transmitter_helper.py | 68 ++++++++------- 16 files changed, 411 insertions(+), 254 deletions(-) diff --git a/data/input_text.txt b/data/input_text.txt index bc56e54..7362506 100644 --- a/data/input_text.txt +++ b/data/input_text.txt @@ -1 +1 @@ -Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient. \ No newline at end of file +A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was. \ No newline at end of file diff --git a/src/fourier_helper.py b/src/fourier_helper.py index 2c89669..ac8483a 100644 --- a/src/fourier_helper.py +++ b/src/fourier_helper.py @@ -12,8 +12,8 @@ def dft_shift(X): obtain a symmetric vector also for even-length signals. Here is a function that does that. (credits: Prandoni P. - Signal processing for Communication course at EPFL) - :param X: the fourier transform of our signal x - :return: a shifted version of the fourier transform (to be around 0) for even and odd length signals + :param X: The fourier transform of our signal x + :return: A shifted version of the fourier transform (to be around 0) for even and odd length signals : """ N = len(X) @@ -34,10 +34,10 @@ def dft_map(X, Fs=params.Fs, shift=True): namely k(F_s/N). Let's remap the DFT coefficients using the sampling rate. (credits: Prandoni P. - Signal processing for Communication course at EPFL) - :param X: the fourier transform of our signal x - :param Fs: the sampling frequency - :param shift: rather we want to shift the fft or not - :return: a real-world-frequency DFT + :param X: The fourier transform of our signal x + :param Fs: The sampling frequency + :param shift: Rather we want to shift the fft or not + :return: A real-world-frequency DFT """ resolution = float(Fs) / len(X) if shift: @@ -52,9 +52,10 @@ def dft_map(X, Fs=params.Fs, shift=True): # TODO awful code, change that ASAP def find_removed_freq_range(samples): """ - Checks which range of frequencies has been removed by the channel (among 1-3kHz, 3-5kHz, 5-7kHz, 7-9kHz) - :param samples: the samples received from the server - :return: the index in params.FREQ_RANGES corresponding to the removed frequency range + Checks which range of frequencies has been removed by the channel (among 1-3kHz, 3-5kHz, 5-7kHz, 7-9kHz) + + :param samples: The samples received from the server + :return: The index in params.FREQ_RANGES corresponding to the removed frequency range """ X = np.fft.fft(samples) f, Y = dft_map(X) @@ -77,9 +78,10 @@ def find_removed_freq_range(samples): def modulate_complex_samples(samples, frequencies): """ Modulate the signal by shifting duplicates of it in the given frequencies - :param samples: the signal to modulate - :param frequencies: the frequencies we want the signal to be duplicated and shifted in - :return: the modulated signals + + :param samples: The signal to modulate + :param frequencies: The frequencies we want the signal to be duplicated and shifted in + :return: The modulated signals """ n_sample = len(samples) time_indices = np.arange(n_sample) / params.Fs @@ -95,10 +97,11 @@ def modulate_complex_samples(samples, frequencies): def demodulate(samples, f): """ - Demodulate the signal - :param samples: the signal to demodulate - :param f: the frequency we want the signal to be modulated with - :return: the demodulated signal + Demodulate the signal from the given frequency, i.e go from pass-band to base-band + + :param samples: The signal to demodulate + :param f: The frequency we want the signal to be modulated with + :return: The demodulated signal """ n_sample = len(samples) time_indices = np.arange(n_sample) / params.Fs diff --git a/src/helper.py b/src/helper.py index 52067f0..ddeab57 100644 --- a/src/helper.py +++ b/src/helper.py @@ -1,15 +1,19 @@ def string2bits(s=''): """ - :param s: the string to be converted - :return: the corresponding array of bits + Converts a string to an array of strings of bytes + + :param s: The string to be converted + :return: The corresponding array of bits """ return [bin(ord(x))[2:].zfill(8) for x in s] def bits2string(b=None): """ - :param b: array of bits to be converted - :return: the corresponding string + Convert an array of strings of bytes to a single string + + :param b: The array of bits to be converted + :return: The corresponding string """ return ''.join([chr(int(x, 2)) for x in b]) @@ -17,9 +21,10 @@ def bits2string(b=None): def compare_messages(message_sent, message): """ Compare 2 messages and print the differences - :param message_sent: the first message to compare - :param message: the second message to compare - :return: None + + :param message_sent: The first message to compare + :param message: The second message to compare + :return: None """ same_length = len(message_sent) == len(message) n_errors = 0 @@ -38,7 +43,3 @@ def compare_messages(message_sent, message): print("({} error(s))".format(n_errors)) return None - -# Intended for testing (to run the program, run main.py) -if __name__ == "__main__": - print("helper.py") diff --git a/src/local_test.py b/src/local_test.py index f685e76..66997c9 100644 --- a/src/local_test.py +++ b/src/local_test.py @@ -2,6 +2,7 @@ import numpy as np from scipy.signal import butter, sosfilt, sosfreqz from scipy.signal import upfirdn +from scrambler import Scrambler import fourier_helper import mappings @@ -62,6 +63,7 @@ def butter_bandpass_filter(data, low_cut_freq, high_cut_freq, Fs=params.Fs, orde def server_simulation(samples, clip=True, filter_freq=True, delay_start=True, delay_end=True, noise=True, scale=True): """ Simulate a server that clips the data to [-1, 1] adds delay, AWGN(0, params.NOISE_VAR), and some garbage at the end + :param scale: rather we scale the samples or not :param noise: rather we noise the signal or not :param delay_end: rather we add delay at the end or not @@ -141,6 +143,7 @@ def server_simulation(samples, clip=True, filter_freq=True, delay_start=True, de def local_test(): """ Test the design locally with modulation and demodulation + :return: None """ data_symbols = transmitter.encoder(mappings.choose_mapping()) @@ -306,8 +309,3 @@ def local_test(): message_file = open(params.input_message_file_path) message_sent = message_file.readline() print(message_received == message_sent) - - -# Intended for testing (to run the program, run main.py) -if __name__ == "__main__": - local_test() diff --git a/src/mappings.py b/src/mappings.py index f3cad66..da5f101 100644 --- a/src/mappings.py +++ b/src/mappings.py @@ -6,8 +6,10 @@ def qam_map(M): """ - :param M: the size of our mapping, i.e the number of symbols we can send - :return: the array of symbols of the M-QAM (Quadrature Amplitude Modulation) + Compute the array of symbols that correspond to a M-QAM mapping + + :param M: The size of our mapping, i.e the number of symbols we can send + :return: The array of symbols of the M-QAM (Quadrature Amplitude Modulation) """ log_sqrt_m = np.log2(np.sqrt(M)) if log_sqrt_m != np.ceil(log_sqrt_m): @@ -19,41 +21,24 @@ def qam_map(M): return [-3-3j, -3-1j, -3+3j, -3+1j, -1-3j, -1-1j, -1+3j, -1+1j, 3-3j, 3-1j, 3+3j, 3+1j, 1-3j, 1-1j, 1+3j, 1+1j] else: raise ValueError("This mapping does not exist yet... He he he") - # N = np.sqrt(M) - 1 - # aux = np.arange(-N, N + 1, 2) - # x, y = np.meshgrid(aux[::-1], aux[::-1]) - # a = (x + y * 1j).T - # size_a = len(a) ** 2 - # b = np.zeros(size_a, dtype=complex) - # c = 0 - # i = 0 - # j = 0 - # while c < size_a: - # b[c] = a[j][i] - # c += 1 - # if (i == len(a) - 1 and j % 2 == 0) or (i == 0 and j % 2 == 1): - # j += 1 - # continue - # if j % 2 == 1: - # i -= 1 - # else: - # i += 1 - # return b - def psk_map(M): """ - :param M: the size of our mapping, i.e the number of symbols we can send - :return: the array of symbols of the M-PSK (Pulse Shift Keying) + Compute the array of symbols that correspond to a M-PSK mapping + + :param M: The size of our mapping, i.e the number of symbols we can send + :return: The array of symbols of the M-PSK (Pulse Shift Keying) """ return np.exp(1j * 2 * np.pi * np.arange(0, M) / M) def pam_map(M): """ - :param M: the size of our mapping, i.e the number of symbols we can send - :return: the array of symbols of the M-PAM (Pulse Amplitude Modulation) + Compute the array of symbols that correspond to a M-PAM mapping + + :param M: The size of our mapping, i.e the number of symbols we can send + :return: The array of symbols of the M-PAM (Pulse Amplitude Modulation) """ if M % 2 != 0: raise ValueError('Parameter[M] is not even.') @@ -63,7 +48,10 @@ def pam_map(M): def choose_mapping(normalize=params.NORMALIZE_MAPPING): """ - :return: The mapping corresponding to the given mapping + Choose the mapping according to the parameters in the file params.py + + :param normalize: Rather we normalize the mapping or not + :return: The corresponding array of symbols """ if params.MAPPING == "qam": chosen_mapping = qam_map(params.M) @@ -88,7 +76,3 @@ def choose_mapping(normalize=params.NORMALIZE_MAPPING): return chosen_mapping - -# Intended for testing (to run the program, run main.py) -if __name__ == "__main__": - print(qam_map(16)) diff --git a/src/parameter_estim.py b/src/parameter_estim.py index 061d2d3..86e82c7 100644 --- a/src/parameter_estim.py +++ b/src/parameter_estim.py @@ -9,9 +9,9 @@ def ML_theta_estimation(received_signal, preamble_samples): Synchronizes the received signal, i.e returns the number of samples after which the first preamble is detected, and the number of samples after which the second preamble is detected. - :param received_signal: signal received from the server - :param preamble_samples: real-valued sequence used to synchronize the received signal - :return: delay in number of samples + :param received_signal: The signal received from the server + :param preamble_samples: The real-valued sequence used to synchronize the received signal + :return: The delay in number of samples """ # Correlation between the received signal and the preamble to find the delay if len(received_signal) >= len(preamble_samples): @@ -35,9 +35,10 @@ def ML_phase_scaling_estim(preamble_samples, preamble_samples_received): """ Estimate the phase shift and the scaling factor applied by the server to the samples (to be called after finding the right theta for synchronization) - :param preamble_samples: the preamble the receiver knows - :param preamble_samples_received: the received preamble - :return: the phase shift and the scaling factor estimates + + :param preamble_samples: The preamble the receiver knows + :param preamble_samples_received: The received preamble + :return: The phase shift and the scaling factor estimates """ dot_product = np.dot(preamble_samples, preamble_samples_received) diff --git a/src/params.py b/src/params.py index 6af56d8..15e64d2 100644 --- a/src/params.py +++ b/src/params.py @@ -70,6 +70,11 @@ def choose_symbol_period(): def params_log(): + """ + Insert the important parameters at the beginning of the logs + + :return: None + """ print("--------------------------------------------------------") print("-----------------------PARAMETERS-----------------------") print("--------------------------------------------------------") @@ -105,3 +110,4 @@ def params_log(): print("--------------------------------------------------------") print("--------------------------------------------------------") print() + return None diff --git a/src/plot_helper.py b/src/plot_helper.py index be7b24e..9d082e3 100644 --- a/src/plot_helper.py +++ b/src/plot_helper.py @@ -7,11 +7,13 @@ def plot_complex_symbols(complex_values, title, color="black", annotate=False): """ - :param complex_values: array of complex values to plot - :param title: title of the plot - :param color: color of the points - :param annotate: rather we annotate the symbols or not - :return: None (plot the graph) + Plot the complex values as black circles in a 2-D (complex) space + + :param complex_values: The array of complex values to plot + :param title: The title of the plot + :param color: The color of the points + :param annotate: Rather we annotate the symbols or not + :return: None """ re = [x.real for x in complex_values] im = [x.imag for x in complex_values] @@ -44,10 +46,12 @@ def plot_complex_symbols(complex_values, title, color="black", annotate=False): def plot_complex_function(complex_values, title, dots=False): """ - :param complex_values: complex values (e.g at the output of the pulse-shaping) - :param title: title of the plot - :param dots: rather we want dots or not - :return: None (plot the graph) + Plot a function of complex values + + :param complex_values: The complex values to plot (e.g at the output of the pulse-shaping) + :param title: The title of the plot + :param dots: Rather we want dots or not + :return: None """ re = [x.real for x in complex_values] im = [x.imag for x in complex_values] @@ -70,8 +74,9 @@ def plot_complex_function(complex_values, title, dots=False): def vertical_lines_frequency_ranges(plot): """ Plots 4 vertical red lines showing the frequency ranges we are interested in - :param plot: the current plot - :return: + + :param plot: The current plot + :return: None """ for i in np.arange(len(params.FREQ_RANGES)): if i == 0: @@ -84,12 +89,13 @@ def vertical_lines_frequency_ranges(plot): def two_fft_plots(samples_1, samples_2, title, y_label_1, y_label_2): """ Plots 2 fft plots, one above the other - :param samples_1: the first sample array - :param samples_2: the second sample array - :param title: the title for both plots - :param y_label_1: the first y axis label - :param y_label_2: the second y axis label - :return: None + + :param samples_1: The first sample array + :param samples_2: The second sample array + :param title: The title for both plots + :param y_label_1: The first y axis label + :param y_label_2: The second y axis label + :return: None """ X = np.fft.fft(samples_1) Y = np.fft.fft(samples_2) @@ -118,13 +124,14 @@ def two_fft_plots(samples_1, samples_2, title, y_label_1, y_label_2): def two_simple_plots(samples_1, samples_2, title, y_label_1, y_label_2): """ - Plots 2 simple plots, one above the other - :param samples_1: the first sample array - :param samples_2: the second sample array - :param title: the title for both plots - :param y_label_1: the first y axis label - :param y_label_2: the second y axis label - :return: None + Plot 2 simple plots, one above the other + + :param samples_1: The first sample array + :param samples_2: The second sample array + :param title: The title for both plots + :param y_label_1: The first y axis label + :param y_label_2: The second y axis label + :return: None """ fig, axs = plt.subplots(2, 1) fig.suptitle(title) @@ -147,10 +154,11 @@ def two_simple_plots(samples_1, samples_2, title, y_label_1, y_label_2): def fft_plot(samples, title, shift=False): """ Plots a simple fft plot - :param shift: rather we shift the fft or not - :param samples: the sample array - :param title: the title for the plot - :return: None + + :param samples: The sample array + :param title: The title for the plot + :param shift: Rather we shift the fft or not + :return: None """ X = np.fft.fft(samples) f_x, y_x = fourier_helper.dft_map(X, shift=shift) @@ -178,10 +186,11 @@ def fft_plot(samples, title, shift=False): def simple_plot(x_axis, y_axis, title): """ Plots a simple plot - :param x_axis: the x axis - :param y_axis: the y axis - :param title: the title of the plot - :return: None + + :param x_axis: The x axis + :param y_axis: The y axis + :param title: The title of the plot + :return: None """ plt.plot(x_axis, y_axis) @@ -198,11 +207,12 @@ def simple_plot(x_axis, y_axis, title): def simple_and_fft_plots(time_indices, samples, title, shift=False): """ Plot a simple and an fft plot of the samples - :param time_indices: time array for the samples - :param samples: the samples to plot - :param title: the title of the plot - :param shift: rather we shift the fft or not - :return: None + + :param time_indices: The time array for the samples + :param samples: The samples to plot + :param title: The title of the plot + :param shift: Rather we shift the fft or not + :return: None """ fig, axs = plt.subplots(2, 1) fig.suptitle(title) @@ -227,8 +237,18 @@ def simple_and_fft_plots(time_indices, samples, title, shift=False): return None -def samples_fft_plots(samples, title, shift=False, time=False, complex=True): - num_plots = 2 if not complex else 3 +def samples_fft_plots(samples, title, shift=False, time=False, is_complex=True): + """ + Plot the samples (2 plots if is_complex=True) and the Fourier transform + + :param samples: The samples to plot + :param title: The title of the plot + :param shift: Rather we shift the Fourier transform plot or not + :param time: Rather we put the time in seconds or the number of samples + :param is_complex: Rather the samples are complex or not + :return: None + """ + num_plots = 2 if not is_complex else 3 fig, axs = plt.subplots(num_plots, 1) fig.suptitle(title) @@ -270,6 +290,14 @@ def samples_fft_plots(samples, title, shift=False, time=False, complex=True): def delay_plots(samples, delay, title): + """ + Plot the samples and a vertical black line at the position of the estimated delay + + :param samples: The samples to plot + :param delay: The estimated delay + :param title: The title of the plot + :return: None + """ fig, axs = plt.subplots(3, 1) fig.suptitle(title) x_axis = np.arange(len(samples[0])) / params.Fs @@ -289,6 +317,14 @@ def delay_plots(samples, delay, title): def compare_preambles(preamble_received, preamble_sent, title): + """ + Compare the preamble received and the preamble sent in a plot + + :param preamble_received: The preamble received + :param preamble_sent: The preamble sent + :param title: The title of the plot + :return: None + """ fig, axs = plt.subplots(2, 2) fig.suptitle(title) x_axis = np.arange(max(len(preamble_sent), len(preamble_received))) @@ -302,3 +338,4 @@ def compare_preambles(preamble_received, preamble_sent, title): axs[0][1].set_ylabel("Real") axs[1][1].plot(x_axis, np.imag(preamble_received)) axs[1][1].set_ylabel("Imaginary") + return None diff --git a/src/preambles.py b/src/preambles.py index 5c5b022..fb15ff0 100644 --- a/src/preambles.py +++ b/src/preambles.py @@ -7,9 +7,10 @@ def generate_preamble_symbols(n_symbols_to_send): """ - Generate preamble symbols - :param n_symbols_to_send: - :return: + Generate preamble symbols used to synchronize our signal at the receiver + + :param n_symbols_to_send: The number of data symbols to send + :return: None """ if params.PREAMBLE_TYPE == "random": preamble_symbols = generate_random_preamble_symbols(n_symbols_to_send) @@ -31,12 +32,24 @@ def generate_preamble_symbols(n_symbols_to_send): def generate_random_preamble_symbols(n_symbols_to_send): + """ + Generate a random preamble sequence of symbols that come from the chosen mapping + + :param n_symbols_to_send: The number of data symbols to send, as we generate only a fix ratio of preamble symbols + that depend on this number + :return: The preamble symbols generated + """ preamble_symbols = np.random.choice(mappings.choose_mapping(), size=int(np.ceil(n_symbols_to_send * params.PREAMBLE_LENGTH_RATIO))) return preamble_symbols def generate_barker_preamble_symbols(): + """ + Generate a barker sequence of complex symbols + + :return: The barker sequence + """ barker_code_13 = np.array([1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1]) preamble_symbols = np.repeat(barker_code_13, params.BARKER_SEQUENCE_REPETITION) return preamble_symbols + 1j * preamble_symbols diff --git a/src/pulses.py b/src/pulses.py index 0013256..368dabe 100644 --- a/src/pulses.py +++ b/src/pulses.py @@ -6,12 +6,14 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params.Fs, normalize=params.NORMALIZE_PULSE): """ - :param SPAN: number of samples in output - :param beta: rolloff factor (0<=beta<=1) - :param T: symbol period (in seconds) - :param Fs: sampling frequency (in Hz) - :param normalize: rather we normalize the rrc or not - :return: time indices, and 1-dimensional FIR (finite-impulse response) filter coefficients + Generate a root-raised-cosine with the help of the formula in Bixio Rimoldi's book. + + :param SPAN: The number of samples in output + :param beta: The rolloff factor (0 <= beta <= 1) + :param T: The symbol period (in seconds) + :param Fs: The sampling frequency (in Hz) + :param normalize: Rather we normalize the rrc or not + :return: The time indices, and 1-dimensional FIR (finite-impulse response) filter coefficients """ if T <= 0 or SPAN <= 0 or Fs <= 0 or beta < 0 or beta > 1: @@ -21,7 +23,7 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params rrc = np.zeros(SPAN) time_indices = (np.arange(SPAN) - SPAN / 2) * Ts sample_numbers = np.arange(SPAN) - index_0 = None + # index_0 = None if beta == 0: for n in sample_numbers: @@ -45,8 +47,8 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params if abs(t) == forbidden_value_t: rrc[n] = rrc_beta_forbidden_value elif beta == 1 or t == 0: - if t == 0: - index_0 = n + # if t == 0: + # index_0 = n rrc[n] = first_term * (np.cos((1 + beta) * pi * t / T) + second_term) / (1 - third_term * t ** 2) else: rrc[n] = first_term * \ @@ -69,7 +71,3 @@ def root_raised_cosine(SPAN=params.SPAN, beta=params.BETA, T=params.T, Fs=params shift=True) return time_indices, rrc - -# Intended for testing (to run the program, run main.py) -if __name__ == "__main__": - _, pulse = root_raised_cosine(1000) diff --git a/src/read_write.py b/src/read_write.py index 818a516..8768a33 100644 --- a/src/read_write.py +++ b/src/read_write.py @@ -10,6 +10,7 @@ def read_preamble_samples(): """ Read the samples from the preamble sample file + :return: The preamble samples """ preamble_samples_file = open(params.preamble_sample_file_path, "r") @@ -21,6 +22,7 @@ def read_preamble_samples(): def read_preamble_symbols(): """ Read the symbols from the preamble symbols file + :return: The preamble symbols """ preamble_symbol_file = open(params.preamble_symbol_file_path, "r") @@ -32,7 +34,8 @@ def read_preamble_symbols(): def read_message_sent(): """ Read the message that was initially sent - :return: the sent message + + :return: The sent message """ input_message_file = open(params.input_message_file_path) message_sent = input_message_file.readline() @@ -48,8 +51,9 @@ def read_message_sent(): def write_samples(samples): """ Write samples in the input sample file - :param samples: samples array to write in the file - :return: None + + :param samples: The samples array to write in the file + :return: None """ f = open(params.input_sample_file_path, "w") for i in range(len(samples)): @@ -61,8 +65,9 @@ def write_samples(samples): def write_preamble_symbols(preamble_symbols): """ Write preamble samples in the preamble sample file - :param preamble_symbols: preamble samples array to write in the file - :return: None + + :param preamble_symbols: The preamble samples array to write in the file + :return: None """ preamble_symbol_file = open(params.preamble_symbol_file_path, "w") for i in range(len(preamble_symbols)): @@ -73,8 +78,9 @@ def write_preamble_symbols(preamble_symbols): def write_preamble_samples(preamble_samples): """ Write preamble samples in the preamble sample file - :param preamble_samples: preamble samples array to write in the file - :return: None + + :param preamble_samples: The preamble samples array to write in the file + :return: None """ preamble_sample_file = open(params.preamble_sample_file_path, "w") for i in range(len(preamble_samples)): @@ -85,8 +91,9 @@ def write_preamble_samples(preamble_samples): def write_message_received(message): """ Write the message received in the appropriate file - :param message: the message to be stored - :return: None + + :param message: The message to be stored + :return: None """ output_message_file = open(params.output_message_file_path, "w") output_message_file.write(message) @@ -97,10 +104,11 @@ def write_message_received(message): def write_gaussian_noise(duration, mean, std): """ Write a gaussian noise with the given parameters in the input file - :param duration: duration of the noise (in seconds) - :param mean: mean of the gaussian noise - :param std: standard deviation of thr gaussian noise - :return: None + + :param duration: The duration of the noise (in seconds) + :param mean: The mean of the gaussian noise + :param std: The standard deviation of the gaussian noise + :return: None """ f = open(params.input_sample_file_path, "w") n_samples = duration * params.Fs @@ -114,10 +122,11 @@ def write_gaussian_noise(duration, mean, std): def write_sinus(duration, frequencies, scaling_factor=1.): """ Write a sinus in the input sample file, at the given frequency - :param scaling_factor: - :param duration: duration of the sinus (in seconds) - :param frequencies: array of frequencies for the sum of sinus - :return: None + + :param scaling_factor: The scaling factor wo multiply the sinus with + :param duration: The duration of the sinus (in seconds) + :param frequencies: The array of frequencies for the sum of sinus + :return: None """ f = open(params.input_sample_file_path, "w") n_samples = int(np.ceil(duration * params.Fs)) @@ -129,10 +138,3 @@ def write_sinus(duration, frequencies, scaling_factor=1.): f.write(str(samples[i] * scaling_factor) + '\n') f.close() return None - - -# Intended for testing (to run the program, run main.py) -if __name__ == "__main__": - # write_samples(input_samples) - write_gaussian_noise(1, mean=0, std=1 / 4) - # write_sinus(1, [2000, 4000, 6000, 8000], scaling_factor=1/8) diff --git a/src/receiver.py b/src/receiver.py index 7102056..57c772e 100644 --- a/src/receiver.py +++ b/src/receiver.py @@ -1,22 +1,23 @@ -import time -import sys - -import params import mappings import receiver_helper def n_tuple_former(): + """ + Form the n-tuple from the samples received from the server + + :return: The data symbols received from the server and the index of the removed frequency range + """ # Prepare the data samples_received, preamble_samples_sent = receiver_helper.prepare_data() # Find the frequency range that has been removed - removed_freq_range, frequency_ranges_available, indices_available = receiver_helper.find_removed_frequency( + removed_frequency_range, frequency_ranges_available, indices_available = receiver_helper.find_removed_frequency( samples_received) # Demodulation - demodulated_samples = receiver_helper.demodulate(samples_received, removed_freq_range, frequency_ranges_available, - indices_available) + demodulated_samples = receiver_helper.demodulate(samples_received, removed_frequency_range, + frequency_ranges_available, indices_available) # Low pass y = receiver_helper.low_pass(demodulated_samples, indices_available) @@ -48,15 +49,16 @@ def n_tuple_former(): # Down-sample the samples to obtain the symbols data_symbols = receiver_helper.downsample(data_samples) - return data_symbols, removed_freq_range + return data_symbols, removed_frequency_range def decoder(symbols, removed_freq_range): """ Map the received symbols to the closest symbols of our mapping - :param removed_freq_range: index of the range that was removed by the server - :param symbols: the observation vector, i.e the received symbols - :return: integers between 0 and M-1, i.e integers corresponding to the bits sent + + :param removed_freq_range: Index of the range that was removed by the server + :param symbols: The observation vector, i.e the received symbols + :return: Integers between 0 and M-1, i.e integers corresponding to the bits sent """ # Choose the mapping according to the params file mapping = mappings.choose_mapping() @@ -68,12 +70,3 @@ def decoder(symbols, removed_freq_range): message_received = receiver_helper.ints_to_message(ints, removed_freq_range) return message_received - -# Intended for testing (to run the program, run main.py) -if __name__ == "__main__": - if params.logs: - moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) - log_file = open("../logs/" + moment + ".log", "w+") - sys.stdout = log_file - data_symbols, removed_freq_range = n_tuple_former() - decoder(data_symbols, removed_freq_range) diff --git a/src/receiver_helper.py b/src/receiver_helper.py index a3eb242..2b0f1f1 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -9,38 +9,57 @@ import read_write +# TODO: Code redundancy to manage + + def prepare_data(): + """ + Prepare the data used to receive the signal, i.e retrieve the received samples and the original preamble + + :return: The received samples and the original preamble + """ if params.logs: print("Preparing the data...") + # Load the input and output samples from their respective files input_samples = np.loadtxt(params.input_sample_file_path) samples_received = np.loadtxt(params.output_sample_file_path) # Plot the input and output samples in Time domain and Frequency domain if params.plots: - plot_helper.samples_fft_plots(input_samples, "Sent samples", complex=False) - plot_helper.samples_fft_plots(samples_received, "Received samples", complex=False) + plot_helper.samples_fft_plots(input_samples, "Sent samples", is_complex=False) + plot_helper.samples_fft_plots(samples_received, "Received samples", is_complex=False) # Read the preamble samples saved previously preamble_samples_sent = read_write.read_preamble_samples() + if params.logs: print("--------------------------------------------------------") return samples_received, preamble_samples_sent def find_removed_frequency(samples_received): + """ + Find the frequency range that has been removed by the server and return the index to the range in params.FREQ_RANGES + + :param samples_received: The samples received from the server + :return: The removed frequency range as an index of params.FREQ_RANGES, and two helping arrays, + i.e an array of booleans where True indicates that the corresponding frequency range + is available, and False that the corresponding frequency range is removed, and an + array of the indices of the available frequency ranges + """ if params.logs: print("Finding the frequency range that has been removed...") _, removed_freq_range = fourier_helper.find_removed_freq_range(samples_received) if params.logs: print("Removed frequency range: {} (range {})".format(removed_freq_range, removed_freq_range + 1)) - # Array of the form [True, True, False, True], where False means that the 3rd frequency range is removes here frequency_ranges_available = [True if i != removed_freq_range else False for i in range(len(params.FREQ_RANGES))] indices_available = [] for i in range(len(frequency_ranges_available)): if frequency_ranges_available[i]: indices_available.append(i) + if params.logs: # print("Frequency ranges available boolean array: {}".format(frequency_ranges_available)) # print("Available indices array: {}".format(indices_available)) @@ -49,6 +68,16 @@ def find_removed_frequency(samples_received): def demodulate(samples_received, removed_freq_range, frequency_ranges_available, indices_available): + """ + Perform a demodulation of the signal from pass-band to base-band. + + :param samples_received: The filtered samples received from the server + :param removed_freq_range: The index of the removed frequency range + :param frequency_ranges_available: An array of booleans where True indicates that the corresponding frequency range + is available + :param indices_available: An array of the indices of the available frequency ranges + :return: The demodulated samples + """ if params.logs: print("Demodulation...") if params.MOD == 1 or params.MOD == 2: @@ -90,6 +119,13 @@ def demodulate(samples_received, removed_freq_range, frequency_ranges_available, def low_pass(demodulated_samples, indices_available): + """ + Low pass the demodulated samples to get rid of the noise and the other frequencies + + :param demodulated_samples: The samples received from the server, demodulated (i.e base-band) + :param indices_available: An array of the indices of the available frequency ranges + :return: The demodulated samples low-passed, i.e the only frequencies of interest for the samples + """ if params.logs: print("Low passing...") _, h = pulses.root_raised_cosine() @@ -107,8 +143,8 @@ def low_pass(demodulated_samples, indices_available): y.append(np.convolve(demodulated_samples[i], h_matched)) if params.plots: for i in range(len(y)): - plot_helper.samples_fft_plots( - y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) + plot_helper.samples_fft_plots( + y[i], "Low-passed samples {}".format(indices_available[i]), shift=True) if params.logs: print("Y shape:") for i in range(len(y)): @@ -120,19 +156,28 @@ def low_pass(demodulated_samples, indices_available): return y -def find_delay(y, preamble_samples_sent, frequency_ranges_available): +def find_delay(samples, preamble_samples_sent, frequency_ranges_available): + """ + Find the delay + + :param samples: The received samples form the server, demodulated and low-passed + :param preamble_samples_sent: The preamble samples used as a synchronization sequence, and previously stored + :param frequency_ranges_available: An array of booleans where True indicates that the corresponding frequency range + is available + :return: The delay found in number of samples + """ if params.logs: print("Finding the delay...") if params.MOD == 1 or params.MOD == 2: - delay = parameter_estim.ML_theta_estimation(y, preamble_samples_sent) + delay = parameter_estim.ML_theta_estimation(samples, preamble_samples_sent) elif params.MOD == 3: delays = [] - for i in range(len(y)): + for i in range(len(samples)): if frequency_ranges_available[i]: - delays.append(parameter_estim.ML_theta_estimation(y[i], preamble_samples_sent)) + delays.append(parameter_estim.ML_theta_estimation(samples[i], preamble_samples_sent)) delay = int(np.round(np.mean(delays))) if params.plots: - plot_helper.delay_plots(y, delay, "Delays estimated (only real part is shown)") + plot_helper.delay_plots(samples, delay, "Delays estimated (only real part is shown)") else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -141,12 +186,23 @@ def find_delay(y, preamble_samples_sent, frequency_ranges_available): return delay -def extract_preamble_samples(y, delay, preamble_samples_sent, frequency_ranges_available, indices_available): +def extract_preamble_samples(samples, delay, preamble_samples_sent, frequency_ranges_available, indices_available): + """ + Extract the preamble samples from the received samples (demodulated and low-passed) + + :param samples: The received samples (previously demodulated and low-passed) + :param delay: The delay found previously in number of samples + :param preamble_samples_sent: The preamble samples used as a synchronization sequence, and previously stored + :param frequency_ranges_available: An array of booleans where True indicates that the corresponding frequency range + is available + :param indices_available: An array of the indices of the available frequency ranges + :return: The preamble samples received + """ if params.logs: print("Extracting the preamble samples...") len_preamble_samples_sent = len(preamble_samples_sent) if params.MOD == 1 or params.MOD == 2: - preamble_samples_received = y[delay:delay + len_preamble_samples_sent] + preamble_samples_received = samples[delay:delay + len_preamble_samples_sent] if params.plots: plot_helper.two_simple_plots(np.real(preamble_samples_received), np.real(preamble_samples_sent), "Preamble samples received vs preamble samples sent", "received", "expected") @@ -155,8 +211,8 @@ def extract_preamble_samples(y, delay, preamble_samples_sent, frequency_ranges_a print("Number of samples for the received preamble: {} samples".format(len(preamble_samples_received))) elif params.MOD == 3: preamble_samples_received = [] - for i in range(len(y)): - preamble_samples_received.append(y[i][delay:delay + len_preamble_samples_sent]) + for i in range(len(samples)): + preamble_samples_received.append(samples[i][delay:delay + len_preamble_samples_sent]) if params.plots: for i in range(len(preamble_samples_received)): if frequency_ranges_available[i]: @@ -172,6 +228,15 @@ def extract_preamble_samples(y, delay, preamble_samples_sent, frequency_ranges_a def estimate_parameters(preamble_samples_sent, preamble_samples_received, indices_available): + """ + Estimate the phase shift and the scaling factor by comparing the preamble samples received and the preamble samples + sent + + :param preamble_samples_sent: The preamble samples originally sent by the transmitter + :param preamble_samples_received: The preamble samples received from the server, extracted previously + :param indices_available: An array of the indices of the available frequency ranges + :return: The estimations of the phase shift and the scaling factor + """ if params.logs: print("Computing the phase shift and the scaling factor...") # Remove SPAN/2 samples in the end because there is still data there for the received preamble @@ -204,20 +269,31 @@ def estimate_parameters(preamble_samples_sent, preamble_samples_received, indice return phase_shift_estim, scaling_factor_estim -def crop_samples_1(y, delay, len_preamble_samples_sent, indices_available): +def crop_samples_1(samples, delay, len_preamble_samples_sent, indices_available): + """ + Crop the samples by removing the delay, the preamble, and adjusting to the first symbol of data + + :param samples: The received samples (previously demodulated and low-passed) + :param delay: The delay computed previously (representing the number of samples before the + apparition of the first preamble) + :param len_preamble_samples_sent: The length in number of samples of the preamble originally sent + :param indices_available: An array of the indices of the available frequency ranges + :return: The samples cropped, i.e the samples representing the data, the ending preamble, + and the noise at the end + """ if params.logs: print("Cropping the samples (removing the delay, the preamble, " "and adjusting to the first relevant sample of data)...") - half_span_h = int(params.SPAN/2) + half_span_h = int(params.SPAN / 2) if params.MOD == 1 or params.MOD == 2: - data_samples = y[delay + len_preamble_samples_sent - half_span_h + params.USF:] + data_samples = samples[delay + len_preamble_samples_sent - half_span_h + params.USF:] if params.plots: - plot_helper.plot_complex_function(y, "y before removing anything") + plot_helper.plot_complex_function(samples, "y before removing anything") plot_helper.plot_complex_function(data_samples, "y after removing the delay, the preamble, and adjusting") elif params.MOD == 3: data_samples = [] - for i in range(len(y)): - data_samples.append(y[i][delay + len_preamble_samples_sent - 1 - half_span_h + 1 + params.USF:]) + for i in range(len(samples)): + data_samples.append(samples[i][delay + len_preamble_samples_sent - 1 - half_span_h + 1 + params.USF:]) if params.plots: for i in range(len(data_samples)): plot_helper.plot_complex_function(data_samples[i], @@ -226,12 +302,19 @@ def crop_samples_1(y, delay, len_preamble_samples_sent, indices_available): else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: - print(params.USF) print("--------------------------------------------------------") return data_samples def find_second_preamble_index(data_samples, preamble_samples_sent): + """ + Find the number of samples before the beginning of the preamble at the end of the samples originally sent + + :param data_samples: The samples containing the data, the preamble at the end, and the noise at the very + end + :param preamble_samples_sent: The preamble samples originally sent by the transmitter + :return: The number of samples before the beginning of the preamble at the end + """ if params.logs: print("Finding the second preamble index...") if params.MOD == 1 or params.MOD == 2: @@ -240,8 +323,8 @@ def find_second_preamble_index(data_samples, preamble_samples_sent): elif params.MOD == 3: second_preamble_index = [] for i in range(len(data_samples)): - second_preamble_index.append(parameter_estim.ML_theta_estimation( - data_samples[i], preamble_samples=preamble_samples_sent[::-1])) + second_preamble_index.append(parameter_estim.ML_theta_estimation( + data_samples[i], preamble_samples=preamble_samples_sent[::-1])) second_preamble_index = int(np.round(np.mean(second_preamble_index))) else: raise ValueError('This modulation type does not exist yet... He he he') @@ -252,9 +335,18 @@ def find_second_preamble_index(data_samples, preamble_samples_sent): def crop_samples_2(data_samples, second_preamble_index): + """ + Crop the data samples to remove the preamble at the end (and the noise at the end by the same occasion), and adjust + to the last sample of data + + :param data_samples: The samples containing the data, the preamble at the end, and the noise at the very + end + :param second_preamble_index: The number of samples before the beginning of the preamble at the end + :return: The data samples + """ if params.logs: print("Cropping the samples (removing the garbage at the end)...") - half_span_h = int(params.SPAN/2) + half_span_h = int(params.SPAN / 2) if params.MOD == 1 or params.MOD == 2: data_samples = data_samples[:second_preamble_index + half_span_h - params.USF + 1] if params.plots: @@ -273,6 +365,13 @@ def crop_samples_2(data_samples, second_preamble_index): def correct_params(data_samples, phase_shift_estim): + """ + Correct the phase shift and the scaling on the data samples + + :param data_samples: The data samples + :param phase_shift_estim: The phase shift estimation computed previously + :return: The data samples phase shift corrected + """ if params.logs: print("Correcting the phase shift and the scaling factor (not yet) on the data samples...") if params.MOD == 1 or params.MOD == 2: @@ -288,6 +387,12 @@ def correct_params(data_samples, phase_shift_estim): def downsample(data_samples): + """ + Down-sample the data samples to obtain the received symbols + + :param data_samples: The data samples + :return: The data symbols received + """ if params.logs: print("Down-sampling...") if params.MOD == 1 or params.MOD == 2: @@ -315,6 +420,13 @@ def downsample(data_samples): def symbols_to_ints(symbols, mapping): + """ + Map the received symbols to the indices of the closest symbol in the chosen mapping + + :param symbols: The received symbols from the server + :param mapping: The chosen mapping for the transmission + :return: The corresponding indices to the received symbols + """ if params.logs: print("Mapping symbols to integers...") if params.MOD == 1 or params.MOD == 2: @@ -332,6 +444,14 @@ def symbols_to_ints(symbols, mapping): def symbols_to_ints_helper(symbols, mapping): + """ + Help to perform the mapping from the received symbols to the indices of the closest symbol in the chosen mapping + (to avoid code redundancy) + + :param symbols: The received symbols from the server + :param mapping: The chosen mapping for the transmission + :return: The corresponding indices to the received symbols + """ # Make sure symbols and mapping have less or equal than 2 dimensions if len(np.shape(symbols)) > 2 or len(np.shape(mapping)) > 2: raise AttributeError("One of the vectors symbols and mapping has more than 2 dimensions!") @@ -360,6 +480,13 @@ def symbols_to_ints_helper(symbols, mapping): def ints_to_message(ints, removed_freq_range): + """ + Convert the integers (indices of the chosen mapping) to the final received message + + :param ints: The indices of the symbols that are the closest to the received symbols + :param removed_freq_range: The removed frequency range by the server (to know if we use the parity check or not) + :return: The received message + """ if params.MOD == 1 or params.MOD == 2: # Convert the ints to BITS_PER_SYMBOL bits bits_separated = ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints] @@ -425,7 +552,8 @@ def ints_to_message(ints, removed_freq_range): for j in range(len(parity_check)): reconstructed_bit_stream.append(0 if parity_check[j] % 2 == 0 else 1) if params.logs: - print("Missing bit stream: {}\n{}\n".format(np.shape(reconstructed_bit_stream), reconstructed_bit_stream)) + print( + "Missing bit stream: {}\n{}\n".format(np.shape(reconstructed_bit_stream), reconstructed_bit_stream)) bit_streams_to_use = bits_separated[:len(bits_separated) - 1] bit_streams_to_use.insert(removed_freq_range, reconstructed_bit_stream) diff --git a/src/scrambler.py b/src/scrambler.py index 914a472..e1a64f0 100644 --- a/src/scrambler.py +++ b/src/scrambler.py @@ -2,6 +2,8 @@ Credits: Prandoni P. - Signal processing for Communication course at EPFL """ +# TODO: Maybe use it to scramble the bits before sending them, and descramble the bits at the receiver + class Scrambler: def __init__(self): diff --git a/src/transmitter.py b/src/transmitter.py index e994548..8f9a887 100644 --- a/src/transmitter.py +++ b/src/transmitter.py @@ -1,9 +1,5 @@ -import time -import sys import numpy as np -import params -import pulses import read_write import transmitter_helper @@ -12,7 +8,8 @@ def encoder(): """ Encode a message into a sequence of symbols according to the given mapping - :return: the corresponding symbols for the message + + :return: The corresponding symbols for the message """ # Retrieve the message from the file as bytes message_bytes = transmitter_helper.retrieve_message_as_bytes() @@ -25,9 +22,11 @@ def encoder(): def waveform_former(h, data_symbols): """ - :param h: the sampled pulse - :param data_symbols: the data symbols modulating the pulse - :return: the samples of a modulated pulse train to send to the server + Shape the data symbols with the pulse h + + :param h: The pulse used to shape the symbols + :param data_symbols: The data symbols modulating the pulse + :return: The samples of a modulated pulse train to send to the server """ # Generate the preamble_symbols and write them in the appropriate file preamble_symbols = transmitter_helper.generate_preamble_to_transmit(len(data_symbols)) @@ -54,26 +53,10 @@ def waveform_former(h, data_symbols): def send_samples(): - transmitter_helper.send_samples() - - -# Intended for testing (to run the program, run main.py) -if __name__ == '__main__': - if params.logs: - moment = time.strftime("%Y-%b-%d__%H_%M_%S", time.localtime()) - log_file = open("../logs/" + moment + ".log", "w+") - sys.stdout = log_file - params.params_log() - - # Encode the message - symbols = encoder() - - # Generate the root-raised_cosine - _, h_pulse = pulses.root_raised_cosine() - - # Construct the samples to send - waveform_former(h_pulse, symbols) + """ + Send the samples to the server, and received the output samples in the corresponding file - # Send the samples to the server + :return: None + """ transmitter_helper.send_samples() - + return None diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 50c3669..e103e46 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -15,6 +15,7 @@ def retrieve_message_as_bytes(): """ Retrieve the string message in the appropriate file given by the params file, and return the bits corresponding to the message as an array of 0 and 1 + :return: The bits corresponding to the message as an array of 0 and 1 """ if params.logs: @@ -27,8 +28,7 @@ def retrieve_message_as_bytes(): new_message_bytes_grouped = ''.join(new_bytes) if params.logs: - print("Sent message:\n{}".format(message)) - print("Length: {} characters".format(len(message))) + print("Sent message ({} characters):\n{}".format(len(message), message)) print("Corresponding bytes:\n{}".format(message_bytes)) print("New bytes:\n{}".format(new_bytes)) print("--------------------------------------------------------") @@ -38,14 +38,16 @@ def retrieve_message_as_bytes(): def grouped_bytes_to_symbols(grouped_bytes): """ Associate the message bits to an array of corresponding symbols that depend on the chosen mapping - :param grouped_bytes: The bits corresponding to the message as an array of 0 and 1 - :return: The corresponding symbols to the given message as an array + + :param grouped_bytes: The bits corresponding to the message as an array of 0 and 1 + :return: The corresponding symbols to the given message as an array """ if params.logs: print("Mapping message bytes to the symbols from our mapping...") if params.MOD == 1 or params.MOD == 2: # New structure with bits_per_symbol bits by row - grouped_bytes = [grouped_bytes[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(grouped_bytes), params.BITS_PER_SYMBOL)] + grouped_bytes = [grouped_bytes[i:i + params.BITS_PER_SYMBOL] for i in range(0, len(grouped_bytes), + params.BITS_PER_SYMBOL)] # Convert this new bits sequence to an integer sequence ints = [[int(b, 2) for b in grouped_bytes]] @@ -81,6 +83,7 @@ def grouped_bytes_to_symbols(grouped_bytes): print("Bit streams: {}".format(np.shape(bit_streams))) for i in range(len(bit_streams)): print("{}".format(bit_streams[i])) + print() # Group them by groups of BITS_PER_SYMBOL bits ints = np.zeros((n_bit_streams, int(len_bit_streams / params.BITS_PER_SYMBOL)), dtype=str) @@ -92,7 +95,7 @@ def grouped_bytes_to_symbols(grouped_bytes): ints[i][j] = mapping_index if params.logs: - print("Ints bits stream {}:\n{}".format(ints.shape, ints)) + print("Ints bits stream {}:\n{}\n".format(ints.shape, ints)) else: raise ValueError("This modulation type does not exist yet... He he he") @@ -102,8 +105,7 @@ def grouped_bytes_to_symbols(grouped_bytes): corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] if params.logs: - print("Symbols/n-tuples to be sent:\n{}".format(corresponding_symbols)) - print("Shape of the symbols: {}".format(np.shape(corresponding_symbols))) + print("Symbols/n-tuples to be sent: {}\n{}".format(np.shape(corresponding_symbols), corresponding_symbols)) if params.plots: plot_helper.plot_complex_symbols(corresponding_symbols, "{} data symbols to send" .format(np.shape(corresponding_symbols)), "blue") @@ -115,8 +117,9 @@ def grouped_bytes_to_symbols(grouped_bytes): def generate_preamble_to_transmit(len_data_symbols): """ Generate preamble symbols that will be used to synchronize our signal at the receiver - :param len_data_symbols: The number of symbols to transmit (useful in case of a random preamble) - :return: The preamble symbols that were generated + + :param len_data_symbols: The number of symbols to transmit (useful in case of a random preamble) + :return: The preamble symbols that were generated """ if params.logs: print("Generating the preamble...") @@ -135,10 +138,11 @@ def generate_preamble_to_transmit(len_data_symbols): def concatenate_symbols(preamble_symbols, data_symbols): """ Construct the symbols to send by concatenating the data symbols with the preamble symbols at the beginning and at - th end - :param preamble_symbols: The preamble symbols to stick to the data symbols - :param data_symbols: The symbols that carry the data to send - :return: The symbols that result from the concatenation + the end + + :param preamble_symbols: The preamble symbols to stick to the data symbols + :param data_symbols: The symbols that carry the data to send + :return: The symbols that result from the concatenation """ if params.logs: print("Concatenating everything together (preamble-data-flipped preamble)...") @@ -154,8 +158,7 @@ def concatenate_symbols(preamble_symbols, data_symbols): p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) if params.logs: for i in range(len(p_data_p_symbols)): - print("Total symbols {}: {}".format(i, p_data_p_symbols[i])) - print("Number of total symbols {}: {}".format(i, np.shape(p_data_p_symbols[i]))) + print("Total symbols {}: {}\n{}".format(i, np.shape(p_data_p_symbols[i]), p_data_p_symbols[i])) if params.plots: plot_helper.plot_complex_symbols(p_data_p_symbols[i], "Symbols {}".format(i)) else: @@ -166,9 +169,10 @@ def concatenate_symbols(preamble_symbols, data_symbols): def shape_symbols(h, p_data_p_symbols): """ Use the pulse h to shape the p_data_p_symbols given, and up-sample by USF before - :param h: The pulse to use to do the pulse shaping - :param p_data_p_symbols: The preamble-data-preamble symbols to shape - :return: The preamble-data-preamble sample resulting from the pulse shaping + + :param h: The pulse to use to do the pulse shaping + :param p_data_p_symbols: The preamble-data-preamble symbols to shape + :return: The preamble-data-preamble sample resulting from the pulse shaping """ if params.logs: print("Pulse shaping the symbols...") @@ -176,9 +180,8 @@ def shape_symbols(h, p_data_p_symbols): if params.MOD == 1 or params.MOD == 2: p_data_p_samples = upfirdn(h, p_data_p_symbols, params.USF) if params.logs: - print("Samples: {}".format(p_data_p_samples)) + print("Samples: {}\n{}".format(np.shape(p_data_p_samples), p_data_p_samples)) print("Up-sampling factor: {}".format(params.USF)) - print("Number of samples: {}".format(len(p_data_p_samples))) if params.plots: plot_helper.samples_fft_plots(p_data_p_samples, "Samples after the pulse shaping", shift=True) elif params.MOD == 3: @@ -200,9 +203,10 @@ def shape_symbols(h, p_data_p_symbols): def shape_preamble_samples(h, preamble_symbols): """ Use the pulse h to shape the preamble_symbols given, and up-sample by USF before - :param h: The pulse to use to do the pulse shaping - :param preamble_symbols: The preamble symbols to shape - :return: The preamble sample resulting from the pulse shaping + + :param h: The pulse to use to do the pulse shaping + :param preamble_symbols: The preamble symbols to shape + :return: The preamble sample resulting from the pulse shaping """ if params.logs: print("Shaping the preamble...") @@ -222,8 +226,9 @@ def shape_preamble_samples(h, preamble_symbols): def modulate_samples(p_data_p_samples): """ Modulate the samples to the allowed frequency ranges - :param p_data_p_samples: The samples to modulate - :return: The modulated samples + + :param p_data_p_samples: The samples to modulate + :return: The modulated samples """ if params.logs: print("Choosing the modulation frequencies and modulating the samples...") @@ -242,7 +247,7 @@ def modulate_samples(p_data_p_samples): print("Min and max sample after modulation: ({}, {})".format(min(p_data_p_samples), max(p_data_p_samples))) if params.plots: - plot_helper.samples_fft_plots(p_data_p_samples, "Samples to send", time=True, complex=True, shift=True) + plot_helper.samples_fft_plots(p_data_p_samples, "Samples to send", time=True, is_complex=True, shift=True) elif params.MOD == 3: modulated_samples = [] for i in range(len(p_data_p_samples)): @@ -261,8 +266,9 @@ def modulate_samples(p_data_p_samples): def scale_samples(p_data_p_modulated_samples): """ Scale the samples to fit to the server's constraints - :param p_data_p_modulated_samples: The samples to scale - :return: The samples scaled + + :param p_data_p_modulated_samples: The samples to scale + :return: The samples scaled """ if params.logs: print("Scaling the samples to the server constraints...") @@ -270,7 +276,7 @@ def scale_samples(p_data_p_modulated_samples): ABS_SAMPLE_RANGE if params.logs: - print("Number of samples: {}".format(len(samples_to_send))) + print("Number of samples to send: {}".format(len(samples_to_send))) print("Minimum sample after scaling: {}".format(min(samples_to_send))) print("Maximum sample after scaling: {}".format(max(samples_to_send))) print("--------------------------------------------------------") @@ -280,6 +286,7 @@ def scale_samples(p_data_p_modulated_samples): def send_samples(): """ Launch the client.py file with the correct arguments according to the parameters in the param file + :return: None """ subprocess.call(["python3 client.py" + @@ -294,4 +301,5 @@ def send_samples(): print("Samples sent!") print("--------------------------------------------------------") print("--------------------------------------------------------") + print("--------------------------------------------------------") return None From 94369e4f5052de48b8ab9e38d6d125edb35faea1 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Thu, 30 May 2019 23:02:02 +0200 Subject: [PATCH 17/20] Reformat, prepare for the submission, add the project report --- Project report.pdf | Bin 0 -> 52281 bytes data/input_text.txt | 2 +- src/helper.py | 1 - src/local_test.py | 1 - src/mappings.py | 4 ++-- src/params.py | 2 +- src/plot_helper.py | 2 +- src/pulses.py | 5 ----- src/receiver.py | 1 - src/receiver_helper.py | 12 +++++++----- src/scrambler.py | 1 + src/transmitter_helper.py | 20 +++++++++++--------- 12 files changed, 24 insertions(+), 27 deletions(-) create mode 100644 Project report.pdf diff --git a/Project report.pdf b/Project report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e3768c8561550c03de790340e3f23a5c12ae9977 GIT binary patch literal 52281 zcmb@N1#lcolb}Vmm{}H#n3>UHX0n)R#LQqZGcz-T#mr{S0(`0Asu962dDnh>Tu=iJSUV5?G+#tNRg%tzw^8 zgkUeUyZLH-JFqpBj!{`uv9Z>`u{`i5ru{00S?y$39ezDX(l}w`w4>sZUov-Dij#9! zeLCSmex0e2Qru3-bxl9t%Y%sc^#mW6c>rIM=uAe0VU)t|X#jNl9Rs+#79Qj7L)v}_ zCKp&_ z1F)|lKQ9XWf_;t`d?lj$IL&

Xlk6G3LJ!`2z*8K4a6`zHzZ^up3ZkUxfr}XsQJb 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 Date: Thu, 30 May 2019 23:26:27 +0200 Subject: [PATCH 18/20] improve the logs --- src/params.py | 3 +-- src/receiver_helper.py | 14 ++++++++------ src/transmitter_helper.py | 19 ++++++++++++------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/params.py b/src/params.py index 9ee47b4..dad3e0d 100644 --- a/src/params.py +++ b/src/params.py @@ -13,7 +13,7 @@ def choose_symbol_period(): # General variables -logs = False +logs = True plots = False input_message_file_path = "../data/input_text.txt" output_message_file_path = "../data/output_text.txt" @@ -109,5 +109,4 @@ def params_log(): print("--------------------------------------------------------") print("--------------------------------------------------------") print("--------------------------------------------------------") - print() return None diff --git a/src/receiver_helper.py b/src/receiver_helper.py index 8659036..160b422 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -261,7 +261,7 @@ def estimate_parameters(preamble_samples_sent, preamble_samples_received, indice if params.logs: for i in range(len(phase_shift_estim)): print("Phase shift {}: {}".format(indices_available[i], phase_shift_estim[i])) - print("Scaling factor {}: {}".format(indices_available[i], scaling_factor_estim[i])) + print("Scaling factor {}: {}\n".format(indices_available[i], scaling_factor_estim[i])) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -428,7 +428,7 @@ def symbols_to_ints(symbols, mapping): :return: The corresponding indices to the received symbols """ if params.logs: - print("Mapping symbols to integers...") + print("Mapping symbols to to the mapping indices...") if params.MOD == 1 or params.MOD == 2: ints = symbols_to_ints_helper(symbols, mapping) elif params.MOD == 3: @@ -438,7 +438,6 @@ def symbols_to_ints(symbols, mapping): else: raise ValueError("This modulation type does not exist yet... He he he") if params.logs: - print("Integers:\n{}".format(ints)) print("--------------------------------------------------------") return ints @@ -516,15 +515,18 @@ def ints_to_message(ints, removed_freq_range): bits_grouped_by_bits_per_symbol.append( ["{0:0{bits_per_symbol}b}".format(i, bits_per_symbol=params.BITS_PER_SYMBOL) for i in ints[j]]) if params.logs: - print("Bits grouped by groups of BITS_PER_SYMBOL bits: ({})\n{}\n".format - (np.shape(bits_grouped_by_bits_per_symbol), bits_grouped_by_bits_per_symbol)) + print("Bits grouped by groups of BITS_PER_SYMBOL bits: {}".format( + np.shape(bits_grouped_by_bits_per_symbol))) + for i in range(len(bits_grouped_by_bits_per_symbol)): + print(bits_grouped_by_bits_per_symbol[i]) + print() # Make an array of strings with it bits_grouped = [] for j in range(len(bits_grouped_by_bits_per_symbol)): bits_grouped.append(''.join(bits_grouped_by_bits_per_symbol[j])) if params.logs: - print("Bits grouped: ({})".format(np.shape(bits_grouped))) + print("Bits grouped: {}".format(np.shape(bits_grouped))) for i in range(len(bits_grouped)): print(bits_grouped[i]) print() diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 86f7fcf..92f64b1 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -28,9 +28,9 @@ def retrieve_message_as_bytes(): new_bytes = [b[1:] for b in message_bytes] new_message_bytes_grouped = ''.join(new_bytes) - print("Sent message ({} characters):\n{}".format(len(message), message)) + print("Sent message ({} characters):\n{}\n".format(len(message), message)) if params.logs: - print("Corresponding bytes:\n{}".format(message_bytes)) + print("Corresponding bytes:\n{}\n".format(message_bytes)) print("New bytes:\n{}".format(new_bytes)) print("--------------------------------------------------------") return new_message_bytes_grouped @@ -96,11 +96,13 @@ def grouped_bytes_to_symbols(grouped_bytes): ints[i][j] = mapping_index if params.logs: - print("Ints bits stream {}:\n{}\n".format(ints.shape, ints)) + print("Ints bits stream {}:\n{}".format(ints.shape, ints)) else: raise ValueError("This modulation type does not exist yet... He he he") corresponding_symbols = np.zeros(np.shape(ints), dtype=complex) + if params.logs: + print("--------------------------------------------------------") mapping = mappings.choose_mapping() for i in range(len(ints)): corresponding_symbols[i] = [mapping[int(j)] for j in ints[i]] @@ -152,18 +154,21 @@ def concatenate_symbols(preamble_symbols, data_symbols): p_data_p_symbols = np.concatenate((preamble_symbols, data_symbols[0], preamble_symbols[::-1])) if params.logs: print("Total symbols: {}".format(p_data_p_symbols)) - print("Number of total symbols: {}".format(np.shape(p_data_p_symbols))) + print("Number of symbols in total: {}".format(np.shape(p_data_p_symbols))) elif params.MOD == 3: p_data_p_symbols = [] for i in range(len(data_symbols)): p_data_p_symbols.append(np.concatenate((preamble_symbols, data_symbols[i], preamble_symbols[::-1]))) if params.logs: + print("Shape of the total symbols: {}".format(np.shape(p_data_p_symbols))) + if params.plots: for i in range(len(p_data_p_symbols)): - print("Total symbols {}: {}\n{}".format(i, np.shape(p_data_p_symbols[i]), p_data_p_symbols[i])) - if params.plots: - plot_helper.plot_complex_symbols(p_data_p_symbols[i], "Symbols {}".format(i)) + plot_helper.plot_complex_symbols(p_data_p_symbols[i], "Symbols {}".format(i)) else: raise ValueError("This mapping type does not exist yet... He he he") + + if params.logs: + print("--------------------------------------------------------") return p_data_p_symbols From a3d25a8ddf3d5befcd80246a0178cbfaa1015d43 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Thu, 30 May 2019 23:57:28 +0200 Subject: [PATCH 19/20] improve plots, polish before submission --- src/params.py | 2 +- src/plot_helper.py | 10 +++++----- src/receiver_helper.py | 33 ++++++++++++++++----------------- src/transmitter_helper.py | 6 ++++-- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/params.py b/src/params.py index dad3e0d..21fb541 100644 --- a/src/params.py +++ b/src/params.py @@ -13,7 +13,7 @@ def choose_symbol_period(): # General variables -logs = True +logs = False plots = False input_message_file_path = "../data/input_text.txt" output_message_file_path = "../data/output_text.txt" diff --git a/src/plot_helper.py b/src/plot_helper.py index a71d014..41caca8 100644 --- a/src/plot_helper.py +++ b/src/plot_helper.py @@ -327,15 +327,15 @@ def compare_preambles(preamble_received, preamble_sent, title): """ fig, axs = plt.subplots(2, 2) fig.suptitle(title) - x_axis = np.arange(max(len(preamble_sent), len(preamble_received))) + x_axis = np.arange(max(len(preamble_received), len(preamble_sent))) - axs[0][0].plot(x_axis, np.real(preamble_sent)) + axs[0][0].plot(x_axis, np.real(preamble_received)) axs[0][0].set_ylabel("Real") - axs[1][0].plot(x_axis, np.imag(preamble_sent)) + axs[1][0].plot(x_axis, np.imag(preamble_received)) axs[1][0].set_ylabel("Imaginary") - axs[0][1].plot(x_axis, np.real(preamble_received)) + axs[0][1].plot(x_axis, np.real(preamble_sent)) axs[0][1].set_ylabel("Real") - axs[1][1].plot(x_axis, np.imag(preamble_received)) + axs[1][1].plot(x_axis, np.imag(preamble_sent)) axs[1][1].set_ylabel("Imaginary") return None diff --git a/src/receiver_helper.py b/src/receiver_helper.py index 160b422..c50a8c6 100644 --- a/src/receiver_helper.py +++ b/src/receiver_helper.py @@ -213,12 +213,11 @@ def extract_preamble_samples(samples, delay, preamble_samples_sent, frequency_ra preamble_samples_received = [] for i in range(len(samples)): preamble_samples_received.append(samples[i][delay:delay + len_preamble_samples_sent]) - if params.plots: - for i in range(len(preamble_samples_received)): - if frequency_ranges_available[i]: - plot_helper.compare_preambles(preamble_samples_received[i], preamble_samples_sent, - "Preamble samples received {} vs preamble samples sent" - .format(indices_available[i])) + # if params.plots: + # for i in range(len(preamble_samples_received)): + # plot_helper.compare_preambles(preamble_samples_received[i], preamble_samples_sent, + # "{}: Preamble samples received vs preamble samples sent" + # .format(indices_available[i])) else: raise ValueError('This modulation type does not exist yet... He he he') @@ -248,7 +247,7 @@ def estimate_parameters(preamble_samples_sent, preamble_samples_received, indice preamble_samples_received[:len(preamble_samples_received) - half_span_h]) if params.logs: print("Phase shift: {}".format(phase_shift_estim)) - print("Scaling factor: {}".format(scaling_factor_estim)) + # print("Scaling factor: {}".format(scaling_factor_estim)) elif params.MOD == 3: phase_shift_estim = [] scaling_factor_estim = [] @@ -261,7 +260,7 @@ def estimate_parameters(preamble_samples_sent, preamble_samples_received, indice if params.logs: for i in range(len(phase_shift_estim)): print("Phase shift {}: {}".format(indices_available[i], phase_shift_estim[i])) - print("Scaling factor {}: {}\n".format(indices_available[i], scaling_factor_estim[i])) + # print("Scaling factor {}: {}\n".format(indices_available[i], scaling_factor_estim[i])) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -294,11 +293,11 @@ def crop_samples_1(samples, delay, len_preamble_samples_sent, indices_available) data_samples = [] for i in range(len(samples)): data_samples.append(samples[i][delay + len_preamble_samples_sent - 1 - half_span_h + 1 + params.USF:]) - if params.plots: - for i in range(len(data_samples)): - plot_helper.plot_complex_function(data_samples[i], - "y[{}] after removing the delay, the preamble, and adjusting". - format(indices_available[i])) + # if params.plots: + # for i in range(len(data_samples)): + # plot_helper.plot_complex_function(data_samples[i], + # "Samples {} after removing the delay, the preamble, and adjusting". + # format(indices_available[i])) else: raise ValueError('This modulation type does not exist yet... He he he') if params.logs: @@ -354,9 +353,9 @@ def crop_samples_2(data_samples, second_preamble_index): elif params.MOD == 3: for i in range(len(data_samples)): data_samples[i] = data_samples[i][:second_preamble_index + half_span_h - params.USF + 1] - if params.plots: - for i in range(len(data_samples)): - plot_helper.plot_complex_function(data_samples[i], "y (only data)") + # if params.plots: + # for i in range(len(data_samples)): + # plot_helper.plot_complex_function(data_samples[i], "y (only data)") else: raise ValueError("This modulation type does not exist yet... He he he") if params.logs: @@ -410,7 +409,7 @@ def downsample(data_samples): print("Shape of the received symbols: {}".format(np.shape(data_symbols))) if params.plots: for i in range(len(data_symbols)): - plot_helper.plot_complex_function(data_symbols[i], "y without preamble") + plot_helper.plot_complex_function(data_symbols[i], "Samples without preamble") plot_helper.plot_complex_symbols(data_symbols[i], "Symbols received", annotate=False) else: raise ValueError("This modulation type does not exist yet... He he he") diff --git a/src/transmitter_helper.py b/src/transmitter_helper.py index 92f64b1..373f0b2 100644 --- a/src/transmitter_helper.py +++ b/src/transmitter_helper.py @@ -28,8 +28,10 @@ def retrieve_message_as_bytes(): new_bytes = [b[1:] for b in message_bytes] new_message_bytes_grouped = ''.join(new_bytes) - print("Sent message ({} characters):\n{}\n".format(len(message), message)) - if params.logs: + if not params.logs: + print("Sent message ({} characters):\n{}".format(len(message), message)) + else: + print("Sent message ({} characters):\n{}\n".format(len(message), message)) print("Corresponding bytes:\n{}\n".format(message_bytes)) print("New bytes:\n{}".format(new_bytes)) print("--------------------------------------------------------") From 5832a60edb25bc41fb1a4b70ae1d6c5314957ca3 Mon Sep 17 00:00:00 2001 From: Rayan DAOD Date: Thu, 30 May 2019 23:58:36 +0200 Subject: [PATCH 20/20] Update readme --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 7223eb6..6c82dcb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,4 @@ ## Professor: Emre Telatar ### Teaching assistants: Sepand Kashani, Reka Inovan, Arda Atalik -**Deadline: 31/05/2019** - -Introduction - +**Deadline: 31/05/2019** \ No newline at end of file