Esempio n. 1
0
def choose_mapping(normalize=params.NORMALIZE_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)
    elif params.MAPPING == "psk":
        chosen_mapping = psk_map(params.M)
    elif params.MAPPING == "pam":
        chosen_mapping = pam_map(params.M)
    else:
        raise ValueError('No modulation of this type is defined')

    if normalize:
        chosen_mapping = chosen_mapping / np.sqrt(
            np.mean(np.abs(chosen_mapping)**2))

    if params.logs:
        print("Choosing the mapping...")
        print("Mapping: {}".format(chosen_mapping))
        print("Normalized = {}".format(params.NORMALIZE_MAPPING))
        print("--------------------------------------------------------")
    if params.plots:
        plot_helper.plot_complex_symbols(
            chosen_mapping,
            title="Chosen mapping, normalized={}".format(normalize),
            color="red",
            annotate=True)

    return chosen_mapping
Esempio n. 2
0
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...")

    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
Esempio n. 3
0
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
    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)..."
        )

    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 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)):
                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
Esempio n. 4
0
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:
        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],
                                                  "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")
    if params.logs:
        print("--------------------------------------------------------")
    return data_symbols
Esempio n. 5
0
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
        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]]

        if params.logs:
            print("Cropped and re-structured bits:\n{}".format(grouped_bytes))
            print("Equivalent integers (indices for our mapping):\n{}".format(
                ints))
    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(grouped_bytes) / (n_bit_streams - 1)))

        # Make it even
        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)

        # Fill the bit streams arrays
        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)
        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("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)
        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
                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

        if params.logs:
            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]]

    if params.logs:
        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")
    if params.logs:
        print("--------------------------------------------------------")
    return corresponding_symbols
Esempio n. 6
0
def local_test():
    """
    Test the design locally with modulation and demodulation

    :return: None
    """
    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(data_symbols))
    preamble_symbols = read_write.read_preamble_symbols()

    # Generate the preamble samples
    preamble_samples = upfirdn(h, preamble_symbols, params.USF)
    len_preamble_samples = len(preamble_samples)

    # Concatenate the preamble symbols with the data symbols
    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)

    print("Shaping the preamble...")
    print("Number of symbols for the preamble: {}".format(
        len(preamble_symbols)))
    print("Number of samples for the preamble: {}".format(
        len(preamble_samples)))
    print("--------------------------------------------------------")
    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)

    print("Shaping the preamble-data-preamble...")
    print("Up-sampling factor: {}".format(params.USF))
    print("Number of samples: {}".format(len(total_samples)))
    print("Minimum sample: {}".format(min(total_samples)))
    print("Maximum sample: {}".format(max(total_samples)))
    print("--------------------------------------------------------")
    plot_helper.plot_complex_function(total_samples,
                                      "Total samples in Time domain")
    plot_helper.fft_plot(total_samples,
                         "Total samples in Frequency domain",
                         shift=True)

    # Modulate the total_samples
    if params.MOD == 1:
        samples = fourier_helper.modulate_complex_samples(
            total_samples, params.np.mean(params.FREQ_RANGES, axis=1))
    elif params.MOD == 2:
        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... He he he')

    print("Modulation of the signal...")
    print("Number of samples: {}".format(len(samples)))
    print("Minimum sample after modulation: {}".format(min(samples)))
    print("Maximum sample after modulation: {}".format(max(samples)))
    print("--------------------------------------------------------")
    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)

    # Scale the signal to the range [-1, 1] (with a bit of uncertainty margin, according to params.ABS_SAMPLE_RANGE)
    samples = (samples / (max(abs(samples))) * params.ABS_SAMPLE_RANGE)
    print("Scaling the signal...")
    print("Minimum sample after scaling: {}".format(min(samples)))
    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 ---------------------------------------------------------------------------------------
    # ----------------------------------------------------------------------------------------------------------------

    # Supposed to retrieve the preamble symbols and samples from the appropriate files, but here we got it above
    plot_helper.plot_complex_function(
        samples, "Samples received from the simulated channel, time domain")
    plot_helper.fft_plot(
        samples,
        "Samples received from the simulated channel, frequency domain")

    # Find the frequency range that has been removed
    range_indices, removed_freq_range = fourier_helper.find_removed_freq_range(
        samples)
    print("Removed frequency range: {} (range {})".format(
        removed_freq_range, removed_freq_range + 1))

    # Choose a frequency for demodulation
    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.MOD == 2:
        if removed_freq_range == 0 or removed_freq_range == 1:
            fc = 7000
        else:
            fc = 3000
    else:
        raise ValueError('This modulation type does not exist yet... He he he')

    demodulated_samples = fourier_helper.demodulate(samples, fc)
    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)

    # Match filter (i.e Low-pass)
    h_matched = np.conjugate(h[::-1])
    y = np.convolve(demodulated_samples, h_matched)
    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(
        y, preamble_samples=preamble_samples)
    print("Delay: {} samples".format(delay))
    print("--------------------------------------------------------")

    # Extract the preamble samples
    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")
    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)))

    # Compute the phase offset
    # We remove the rrc-equivalent-tail because there is data on the tail otherwise
    # TODO: why dot works and not vdot (supposed to conjugate the first term in the formula)
    dot_product = np.dot(
        preamble_samples[:len_preamble_samples - half_span_h],
        preamble_samples_received[:len(preamble_samples_received) -
                                  half_span_h])
    print("Dot product: {}".format(dot_product))

    preamble_energy = 0
    for i in range(len_preamble_samples - half_span_h):
        preamble_energy += np.absolute(preamble_samples[i])**2
    print("Energy of the preamble: {}".format(preamble_energy))

    frequency_offset_estim = np.angle(dot_product)
    print("Frequency offset: {}".format(frequency_offset_estim))

    scaling_factor = abs(dot_product) / preamble_energy
    print("Scaling factor: {}".format(scaling_factor))

    # Crop the samples (remove the delay, the preamble, and the ramp-up)
    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])
    print("Second preamble index: {} samples".format(second_preamble_index))
    print("--------------------------------------------------------")

    # Crop the samples (remove the preamble, and the garbage at the end)
    data_samples = data_samples[:second_preamble_index + half_span_h -
                                params.USF + 1]
    plot_helper.plot_complex_function(
        data_samples,
        "y after removing the delay, the preamble, and the ramp-up")

    # TODO: why frequency_offset - pi/2 works ?
    data_samples = data_samples * np.exp(-1j *
                                         (frequency_offset_estim - np.pi / 2))

    # Down-sample the samples to obtain the symbols
    data_symbols = data_samples[::params.USF]
    print("Number of symbols received: {}".format(len(data_symbols)))

    plot_helper.plot_complex_function(data_symbols, "y without preamble")
    plot_helper.plot_complex_symbols(data_symbols,
                                     "Symbols received",
                                     annotate=False)

    # Decode the symbols
    ints = receiver.decoder(data_symbols, mappings.choose_mapping())
    message_received = receiver.ints_to_message(ints)

    message_file = open(params.input_message_file_path)
    message_sent = message_file.readline()
    print(message_received == message_sent)