def add_channel(self, progress_bar=True):

        if progress_bar:
            device_iters = tqdm(
                iterable=range(self.num_devices),
                desc=self.name + " Devices",
                unit="device",
                leave=False)
        else:
            device_iters = range(self.num_devices)

        for n in device_iters:
            signals_temp = self.signals.copy()
            ind_n = np.where(self.devices.argmax(axis=1) == n)[0]
            num_signals_per_channel = len(ind_n)//len(self.seeds)  # per class

            for idx_seed, main_seed in enumerate(self.seeds):
                seed_n = main_seed + n

                for i in ind_n[idx_seed * num_signals_per_channel: (idx_seed+1) * num_signals_per_channel]:
                    signal = real_to_complex(self.signals[i])
                    signal_faded = add_custom_fading_channel(signal, 500, self.parameters["sampling_rate"],
                                                             seed=seed_n,
                                                             beta=0,
                                                             delay_seed=False,
                                                             channel_type=self.parameters["channel_type"],
                                                             channel_method=self.parameters["channel_method"],
                                                             noise_method=self.parameters["noise_method"])
                    signal_faded = normalize(signal_faded)
                    signals_temp[i] = np.concatenate((signal_faded.real.reshape(
                        [-1, 1]), signal_faded.imag.reshape([-1, 1])), axis=1).reshape((1, -1, 2))

        self.signals = signals_temp.copy()
def augment_with_channel(dict_wifi, aug_type, channel_method, num_aug_train, num_aug_test, keep_orig_train, keep_orig_test, num_ch_train, num_ch_test, channel_type_aug_train, channel_type_aug_test, delay_seed_aug_train, snr_train, noise_method, seed_aug, sampling_rate, data_format):

    x_train = dict_wifi['x_train'].copy()
    y_train = dict_wifi['y_train'].copy()

    x_test = dict_wifi['x_test'].copy()
    y_test = dict_wifi['y_test'].copy()

    num_train = dict_wifi['x_train'].shape[0]
    num_test = dict_wifi['x_test'].shape[0]
    num_classes = dict_wifi['y_train'].shape[1]

    # print('\n-------------------------------')

    print('\nChannel augmentation')
    print('\tAugmentation type: {}'.format(aug_type))
    print('\tNo of augmentations: Train: {}, Test: {}\n\tKeep originals: Train: {}, Test: {}'.format(
        num_aug_train, num_aug_test, keep_orig_train, keep_orig_test))
    print('\tNo. of channels per aug: Train: {}, Test: {}'.format(num_ch_train, num_ch_test))
    print('\tChannel type: Train: {}, Test: {}\n'.format(
        channel_type_aug_train, channel_type_aug_test))

    print("Seed: Train: {:}".format(seed_aug))

    x_train_aug = x_train.copy()
    y_train_aug = y_train.copy()

    channel_dict = {}
    for i in range(401):
        channel_dict[i] = seed_aug

    if num_ch_train < -1:
        raise ValueError('num_ch_train')
    elif num_ch_train != 0:
        for k in tqdm(range(num_aug_train)):
            signal_ch = np.zeros(x_train.shape)
            for i in tqdm(range(num_train)):
                signal = x_train[i][:, 0]+1j*x_train[i][:, 1]
                if num_ch_train == -1:
                    signal_faded = add_custom_fading_channel(signal, snr_train, sampling_rate,
                                                             seed=seed_aug +
                                                             (i + k*num_train) % (num_train*num_aug_train),
                                                             beta=0,
                                                             delay_seed=delay_seed_aug_train,
                                                             channel_type=channel_type_aug_train,
                                                             channel_method=channel_method,
                                                             noise_method=noise_method)
                elif aug_type == 1:
                    signal_faded = add_custom_fading_channel(signal, snr_train, sampling_rate,
                                                             seed=channel_dict[np.argmax(
                                                                 y_train[i])],
                                                             beta=0,
                                                             delay_seed=delay_seed_aug_train,
                                                             channel_type=channel_type_aug_train,
                                                             channel_method=channel_method,
                                                             noise_method=noise_method)
                    channel_dict[np.argmax(y_train[i])] += 1
                elif aug_type == 0:
                    signal_faded = add_custom_fading_channel(signal, snr_train, sampling_rate,
                                                             # seed = 0,
                                                             seed=seed_aug + k * num_ch_train + \
                                                             (i %
                                                              num_ch_train),
                                                             beta=0,
                                                             delay_seed=delay_seed_aug_train,
                                                             channel_type=channel_type_aug_train,
                                                             channel_method=channel_method,
                                                             noise_method=noise_method)

                signal_faded = normalize(signal_faded)
                signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])), axis=1).reshape((1, -1, 2))

            if keep_orig_train is False:
                if k == 0:
                    x_train_aug = signal_ch.copy()
                    y_train_aug = y_train.copy()
                else:
                    x_train_aug = np.concatenate((x_train_aug, signal_ch), axis=0)
                    y_train_aug = np.concatenate((y_train_aug, y_train), axis=0)
            else:
                x_train_aug = np.concatenate((x_train_aug, signal_ch), axis=0)
                y_train_aug = np.concatenate((y_train_aug, y_train), axis=0)

    dict_wifi['x_train'] = x_train_aug.copy()
    dict_wifi['y_train'] = y_train_aug.copy()
    dict_wifi['fc_train'] = np.tile(dict_wifi['fc_train'], num_aug_train)

    del x_train, y_train, x_train_aug, y_train_aug

    data_format = data_format + '[aug-{}-art-{}-ty-{}-nch-{}-snr-{:.0f}]-'.format(
        num_aug_train, channel_type_aug_train, aug_type, num_ch_train, snr_train)

    return dict_wifi, data_format
def physical_layer_channel(dict_wifi, phy_method, channel_type_phy_train,
                           channel_type_phy_test, channel_method, noise_method,
                           seed_phy_train, seed_phy_test, sampling_rate,
                           data_format):

    num_train = dict_wifi['x_train'].shape[0]
    num_test = dict_wifi['x_test'].shape[0]
    num_classes = dict_wifi['y_train'].shape[1]

    x_train_orig = dict_wifi['x_train'].copy()
    y_train_orig = dict_wifi['y_train'].copy()

    x_test_orig = dict_wifi['x_test'].copy()
    y_test_orig = dict_wifi['y_test'].copy()

    # --------------------------------------------------------------------------------------------
    # Physical channel simulation (different days)
    # --------------------------------------------------------------------------------------------

    print('\nPhysical channel simulation (different days)')
    print('\tMethod: {}'.format(phy_method))
    print('\tChannel type: Train: {}, Test: {}'.format(channel_type_phy_train,
                                                       channel_type_phy_test))
    print('\tSeed: Train: {}, Test: {}\n'.format(seed_phy_train,
                                                 seed_phy_test))

    if phy_method == 0:  # Same channel for all packets
        signal_ch = dict_wifi['x_train'].copy()
        for i in tqdm(range(num_train)):
            signal = dict_wifi['x_train'][
                i][:, 0] + 1j * dict_wifi['x_train'][i][:, 1]
            signal_faded = add_custom_fading_channel(
                signal,
                500,
                sampling_rate,
                seed=seed_phy_train,
                beta=0,
                delay_seed=False,
                channel_type=channel_type_phy_train,
                channel_method=channel_method,
                noise_method=noise_method)
            signal_faded = normalize(signal_faded)
            signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                          axis=1).reshape((1, -1, 2))
        dict_wifi['x_train'] = signal_ch.copy()

        signal_ch = dict_wifi['x_test'].copy()
        for i in tqdm(range(num_test)):
            signal = dict_wifi['x_test'][i][:,
                                            0] + 1j * dict_wifi['x_test'][i][:,
                                                                             1]
            signal_faded = add_custom_fading_channel(
                signal,
                500,
                sampling_rate,
                seed=seed_phy_test,
                beta=0,
                delay_seed=False,
                channel_type=channel_type_phy_test,
                channel_method=channel_method,
                noise_method=noise_method)
            signal_faded = normalize(signal_faded)
            signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                          axis=1).reshape((1, -1, 2))
        dict_wifi['x_test'] = signal_ch.copy()

    elif phy_method == 1:  # Different channel for each class, same channel for all packets in a class
        signal_ch = dict_wifi['x_train'].copy()
        for n in trange(num_classes):
            ind_n = np.where(y_train_orig.argmax(axis=1) == n)[0]
            seed_phy_train_n = seed_phy_train[0] + n
            # print('{}: {}'.format(n, ind_n))
            for i in ind_n:
                signal = dict_wifi['x_train'][
                    i][:, 0] + 1j * dict_wifi['x_train'][i][:, 1]
                signal_faded = add_custom_fading_channel(
                    signal,
                    500,
                    sampling_rate,
                    seed=seed_phy_train_n,
                    beta=0,
                    delay_seed=False,
                    channel_type=channel_type_phy_train,
                    channel_method=channel_method,
                    noise_method=noise_method)
                signal_faded = normalize(signal_faded)
                # ipdb.set_trace()
                signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                              axis=1).reshape((1, -1, 2))
        dict_wifi['x_train'] = signal_ch.copy()

        signal_ch = dict_wifi['x_test'].copy()
        for n in trange(num_classes):
            ind_n = np.where(y_test_orig.argmax(axis=1) == n)[0]
            seed_phy_test_n = seed_phy_test + n
            for i in ind_n:
                signal = dict_wifi['x_test'][
                    i][:, 0] + 1j * dict_wifi['x_test'][i][:, 1]
                signal_faded = add_custom_fading_channel(
                    signal,
                    500,
                    sampling_rate,
                    seed=seed_phy_test_n,
                    beta=0,
                    delay_seed=False,
                    channel_type=channel_type_phy_test,
                    channel_method=channel_method,
                    noise_method=noise_method)
                signal_faded = normalize(signal_faded)
                signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                              axis=1).reshape((1, -1, 2))
        dict_wifi['x_test'] = signal_ch.copy()

    else:  # train on multiple days, test on 1 day, with a diff channel for each class
        signal_ch = dict_wifi['x_train'].copy()
        for n in trange(num_classes):
            ind_n = np.where(y_train_orig.argmax(axis=1) == n)[0]
            num_signals_per_day = len(ind_n) // phy_method  # per class

            # Day j
            for j in range(phy_method - 1):

                seed_phy_train_n_j = seed_phy_train[j] + n

                for i in ind_n[j * num_signals_per_day:(j + 1) *
                               num_signals_per_day]:
                    signal = dict_wifi['x_train'][
                        i][:, 0] + 1j * dict_wifi['x_train'][i][:, 1]
                    signal_faded = add_custom_fading_channel(
                        signal,
                        500,
                        sampling_rate,
                        seed=seed_phy_train_n_j,
                        beta=0,
                        delay_seed=False,
                        channel_type=channel_type_phy_train,
                        channel_method=channel_method,
                        noise_method=noise_method)
                    signal_faded = normalize(signal_faded)
                    signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                        [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                                  axis=1).reshape((1, -1, 2))

            # Last day
            seed_phy_train_n_j = seed_phy_train[phy_method - 1] + n
            for i in ind_n[(phy_method - 1) * num_signals_per_day:]:
                signal = dict_wifi['x_train'][
                    i][:, 0] + 1j * dict_wifi['x_train'][i][:, 1]
                signal_faded = add_custom_fading_channel(
                    signal,
                    500,
                    sampling_rate,
                    seed=seed_phy_train_n_j,
                    beta=0,
                    delay_seed=False,
                    channel_type=channel_type_phy_train,
                    channel_method=channel_method,
                    noise_method=noise_method)
                signal_faded = normalize(signal_faded)
                signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                              axis=1).reshape((1, -1, 2))
        dict_wifi['x_train'] = signal_ch.copy()

        signal_ch = dict_wifi['x_test'].copy()
        for n in trange(num_classes):
            ind_n = np.where(y_test_orig.argmax(axis=1) == n)[0]
            seed_phy_test_n = seed_phy_test + n
            for i in ind_n:
                signal = dict_wifi['x_test'][
                    i][:, 0] + 1j * dict_wifi['x_test'][i][:, 1]
                signal_faded = add_custom_fading_channel(
                    signal,
                    500,
                    sampling_rate,
                    seed=seed_phy_test_n,
                    beta=0,
                    delay_seed=False,
                    channel_type=channel_type_phy_test,
                    channel_method=channel_method,
                    noise_method=noise_method)
                signal_faded = normalize(signal_faded)
                signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                              axis=1).reshape((1, -1, 2))
        dict_wifi['x_test'] = signal_ch.copy()

    data_format = data_format + \
        '[-phy-{}-m-{}-s-{}]-'.format(channel_type_phy_train, phy_method, np.max(seed_phy_train))

    return dict_wifi, data_format
    def channel_augmentation(self, parameters, progress_bar=True):

    	num_augmentation = parameters["num_augmentation"]
    	snr = parameters["snr"]
    	sampling_rate = parameters["sampling_rate"]

        signals = real_to_complex(self.signals)
        for i, signal in enumerate(signals):
            signal_faded = add_custom_fading_channel(signal, parameters["snr"], parameters["sampling_rate"],
                                                     seed=None,
                                                     beta=0,
                                                     delay_seed=False,
                                                     channel_type=parameters["channel_type"],
                                                     channel_method=parameters["channel_method"],
                                                     noise_method=parameters["noise_method"])
            signals[i] = signal_faded
            self.signals[i] = np.concatenate((signal_faded.real.reshape(
                [-1, 1]), signal_faded.imag.reshape([-1, 1])), axis=1).reshape((1, -1, 2))

        orthogonal_seeds = {}
	    for i in range(401):
	        orthogonal_seeds[i] = self.seeds

	    if progress_bar:
            augmentations_iters = tqdm(
                iterable=range(num_augmentation),
                desc=self.name + " Augmentations",
                unit="augmentation",
                leave=False)
        else:
            augmentations_iters = range(num_augmentation)

        for k in augmentations_iters:
            signals_augmented = np.zeros(self.signals.shape)
            if progress_bar:
	            signals_iters = tqdm(
	                iterable=range(self.num_signals),
	                desc=self.name + " Signals",
	                unit="signal",
	                leave=False)
	        else:
	            signals_iters = range(self.num_signals)

            for i in signals_iters:

                complex_signals = real_to_complex(self.signals)
                if parameters["augmentation_type"]=="full_random":
                    signal_faded = add_custom_fading_channel(complex_signals, parameters["snr"], parameters["sampling_rate"],
                                                     seed=self.seeds +
                                                             (i + k * self.num_signals) % (self.num_signals*num_augmentation),
                                                     beta=0,
                                                     delay_seed=False,
                                                     channel_type=parameters["channel_type"],
                                                     channel_method=parameters["channel_method"],
                                                     noise_method=parameters["noise_method"])
                elif parameters["augmentation_type"]=="orthogonal":
                    signal_faded = add_custom_fading_channel(complex_signals, parameters["snr"], parameters["sampling_rate"],
                                                     seed=orthogonal_seeds[np.argmax(
                                                                 y_train[i])],
                                                     beta=0,
                                                     delay_seed=False,
                                                     channel_type=parameters["channel_type"],
                                                     channel_method=parameters["channel_method"],
                                                     noise_method=parameters["noise_method"])
                    orthogonal_seeds[np.argmax(y_train[i])] += 1
                else:
                	raise NotImplementedError
                signal_faded = normalize(signal_faded)
                signals_augmented[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])), axis=1).reshape((1, -1, 2))

            if parameters["keep_original"] is False:
                if k == 0:
                    signals_aug = signals_augmented.copy()
                    devices_aug = y_train.copy()
                else:
                    signals_aug = np.concatenate((signals_aug, signals_augmented), axis=0)
                    devices_aug = np.concatenate((devices_aug, self.devices), axis=0)
            else:
                signals_aug = np.concatenate((signals_aug, signals_augmented), axis=0)
                devices_aug = np.concatenate((devices_aug, self.devices), axis=0)

        dict_wifi['x_train'] = signals_aug.copy()
	    dict_wifi['y_train'] = devices_aug.copy()
	    dict_wifi['fc_train'] = np.tile(dict_wifi['fc_train'], num_aug_train)
def augment_with_channel_test(dict_wifi, aug_type, channel_method,
                              num_aug_train, num_aug_test, keep_orig_train,
                              keep_orig_test, num_ch_train, num_ch_test,
                              channel_type_aug_train, channel_type_aug_test,
                              delay_seed_aug_test, snr_test, noise_method,
                              seed_aug, sampling_rate, data_format):

    x_test = dict_wifi['x_test'].copy()
    y_test = dict_wifi['y_test'].copy()

    num_train = dict_wifi['x_train'].shape[0]
    num_test = dict_wifi['x_test'].shape[0]
    num_classes = dict_wifi['y_train'].shape[1]

    x_test_aug = x_test.copy()
    y_test_aug = y_test.copy()

    if num_ch_test < -1:
        raise ValueError('num_ch_test')
    elif num_ch_test != 0:
        for k in tqdm(range(num_aug_test)):
            signal_ch = np.zeros(x_test.shape)
            for i in tqdm(range(num_test)):
                signal = dict_wifi['x_test'][
                    i][:, 0] + 1j * dict_wifi['x_test'][i][:, 1]
                if num_ch_test == -1:
                    signal_faded = add_custom_fading_channel(
                        signal,
                        snr_test,
                        sampling_rate,
                        seed=seed_aug + num_train * num_aug_train + 1 +
                        (i + k * num_test) % (num_test * num_aug_test),
                        beta=0,
                        delay_seed=delay_seed_aug_test,
                        channel_type=channel_type_aug_test,
                        channel_method=channel_method,
                        noise_method=noise_method)
                else:
                    signal_faded = add_custom_fading_channel(signal, snr_test, sampling_rate,
                                                             # seed = 1,
                                                             seed=seed_aug + num_train*num_aug_train + \
                                                             1 + (i % num_ch_test) + \
                                                             k * num_ch_test,
                                                             beta=0,
                                                             delay_seed=delay_seed_aug_test,
                                                             channel_type=channel_type_aug_test,
                                                             channel_method=channel_method,
                                                             noise_method=noise_method)

                signal_faded = normalize(signal_faded)
                signal_ch[i] = np.concatenate((signal_faded.real.reshape(
                    [-1, 1]), signal_faded.imag.reshape([-1, 1])),
                                              axis=1)
                # dict_wifi['x_test'][i] = signal_ch
            if keep_orig_test is False:
                if k == 0:
                    x_test_aug = signal_ch.copy()
                    y_test_aug = y_test.copy()
                else:
                    x_test_aug = np.concatenate((x_test_aug, signal_ch),
                                                axis=0)
                    y_test_aug = np.concatenate((y_test_aug, y_test), axis=0)
            else:
                x_test_aug = np.concatenate((x_test_aug, signal_ch), axis=0)
                y_test_aug = np.concatenate((y_test_aug, y_test), axis=0)

    dict_wifi['x_test'] = x_test_aug.copy()
    dict_wifi['y_test'] = y_test_aug.copy()
    dict_wifi['fc_test'] = np.tile(dict_wifi['fc_test'], num_aug_test)

    del x_test_aug, y_test_aug

    return dict_wifi, data_format