Скачиваний:
13
Добавлен:
25.06.2023
Размер:
15.2 Кб
Скачать
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

from config import const_var


def get_corrected_distance(distance, base_station_height, sub_station_height):
    '''Возвращает дистанцию от БС до АБ с учетом высот и расстояния между ними'''
    return (distance**2 + abs(base_station_height - sub_station_height)**2)**.5


def subs_generator(subs_cnt):
    '''Генерация пропускных способностей для всех абанентов в каждом моделируемом слоте'''
    slot_cnt = const_var.get("slot_cnt")    # Кол-во моделируемых слотов
    R        = const_var.get("R")           # Радиус действия базовой станции
    f0       = const_var.get("f0")          # Частота БС
    Ptx      = const_var.get("Ptx")         # Мощность излучения БС
    DF       = const_var.get("DF")          # Полоса пропускания канала связи
    Pn       = const_var.get("Pn")          # Мощность теплового шума
    T_rb     = const_var.get("T_rb")        # Длительность слота
    h_bs     = const_var.get("h_bs")        # Высота базовой станции
    h_rx     = const_var.get("h_rx")        # Высота точки приема
    S        = const_var.get("S")           # small city (S = 0) / large city (S = 3)

    # Таблицы с пропускными способностями абонентов для каждого слота
    subs = pd.DataFrame(columns=range(slot_cnt))

    # Список растояний до АБ в км
    sub_distans = [get_corrected_distance(dist, h_bs, h_rx) / 1000 for dist in np.sqrt(np.random.uniform(0, R ** 2, subs_cnt))]
    # print(sub_distans)

    # Список случайных затуханий сигнала по модели Indoor Propagation Model (ITU)
    # L_list = 10 ** ((20 * np.log10(f0) + 29 * np.log10(sub_distans) + 0 - 28) / 10)

    # Список случайных затуханий сигнала по модели Окамура-Хаты
    a = (1.1 * np.log10(f0) - 0.7) * h_rx - (1.56 * np.log10(f0) - 0.8)
    L_list = 10**((46.3 + 33.9 * np.log10(f0) - 13.82 * np.log10(h_bs) - a + (44.9 - 6.55 * np.log10(h_rx)) * np.log10(sub_distans) + S) / 10)

    # Цикл по пользователям
    for sub_idx, L in enumerate(L_list):
        
        # Расчеты для slot_cnt слотов
        Prx = Ptx / (L + np.random.normal(0, 1, slot_cnt))  # Расчёт мощности
        SNR = Prx / Pn                                      # Сигнал/шум
        CC = DF * np.log2(1 + SNR)  # Макс пропускная способность канала связи

        # Запись расчитанных объемов сообщения которое
        # может быть переданно из буфера на АБ для каждого слота
        subs.loc['sub_' + str(sub_idx)] = CC * T_rb

    return subs



def get_R_mean(slot_resource_blocks, subs, sub_idx, slot_idx):
    '''Возвращает среднюю скорость с которой АБ скачивал данные'''
    cd_slot_cnt = const_var.get("cooldown_slot_count")  # Кол-во слотов перерасчета, средней скорости
    Nrb = const_var.get("Nrb")  # Кол-во ресурсных блоков

    # Объём данных, который передан абоненту sub_idx
    packs_sum = 0

    # Цикл по слотам
    for slot_idx in range(max(0, slot_idx - 1 - cd_slot_cnt), slot_idx):

        # Кол-во ресурсных блоков в слоте slot_idx, которые пренадлежат АБ с индексом sub_idx
        sub_slot_res_block_cnt = 0

        # Цикл по ресурсным блокам
        for res_sub_idx in slot_resource_blocks[slot_idx]:

            # Проверка принадлежности ресурсного блока в слоте АБ sub_idx
            if res_sub_idx == sub_idx:
                sub_slot_res_block_cnt += 1
        
        # Находим объём данных, который передан абоненту sub_idx в ресурсных блоках в слоте slot_idx
        packs_sum += subs[slot_idx][sub_idx] * sub_slot_res_block_cnt
    return packs_sum / cd_slot_cnt


def get_R_mean_smoothing_filter(slot_resource_blocks, subs, sub_idx, slot_idx, sub_R_mean_list):
    '''Возвращает среднюю скорость с которой АБ скачивал данные методом сглаживающего фильтра'''
    cd_slot_cnt = const_var.get("cooldown_slot_count")  # Кол-во слотов перерасчета, средней скорости
    T_rb = const_var.get("T_rb")                        # Длительность слота
    betta       = 1 / cd_slot_cnt   # Параметр сглаживающего фильтра
    
    return (1 - betta) * sub_R_mean_list[sub_idx] + betta * sum([subs[slot_idx][sub_idx] for res_sub_idx in slot_resource_blocks[-1] if res_sub_idx == sub_idx]) / T_rb


def get_sub_index_by_equa_blind(sub_R_mean_list, sub_with_full_buffer):
    '''
    Возвращает индекс АБ для алгоритма Proportional fair, который 
    обеспечивает равные скорости между всеми абонентами
    '''
    # Расчет приоритетов и возвращение максимального
    subs_priority = [1 / sub_R_mean if sub_R_mean > 0 else 1 for sub_R_mean in sub_R_mean_list]
    
    # Получаем список отсортированных индексов АБ по приоритету
    subs_priority_idx = np.argsort(subs_priority)

    # Возвращаем список тех у кого не пустой буфер
    return [sub_idx for sub_idx in subs_priority_idx if sub_idx in sub_with_full_buffer]



def get_sub_index_by_maximum_throughput(subs_slot_CC, sub_with_full_buffer):
    '''
    Возвращает индекс АБ для алгоритма Maximum Throughput, 
    который максимизирует суммарную скорость передачи базовой станцией
    '''
    # Получаем список отсортированных индексов АБ по приоритету
    subs_priority_idx = np.argsort(subs_slot_CC)

    # Возвращаем список тех у кого не пустой буфер
    return [sub_idx for sub_idx in subs_priority_idx if sub_idx in sub_with_full_buffer]


def get_sub_index_by_proportional_fair(subs_slot_CC, sub_R_mean_list, sub_with_full_buffer):
    '''
    Возвращает индекс АБ для алгоритма Proportional fair, который 
    выделяет равные доли ресурсов всем абонентам
    '''
    # Расчет приоритетов и возвращение максимального
    subs_priority = [sub_slot_CC / (sub_R_mean if sub_R_mean > 0 else 1) for sub_slot_CC, sub_R_mean in zip(subs_slot_CC, sub_R_mean_list)]

    # Получаем список отсортированных индексов АБ по приоритету
    subs_priority_idx = np.argsort(subs_priority)

    # Возвращаем список тех у кого не пустой буфер
    return [sub_idx for sub_idx in subs_priority_idx if sub_idx in sub_with_full_buffer]
    


def get_sub_index(subs_slot_CC, algorithm, sub_with_full_buffer, sub_R_mean_list):
    match algorithm:
        case 'Equal Blind':
            return get_sub_index_by_equa_blind(sub_R_mean_list, sub_with_full_buffer)

        case 'Maximum Throughput':
            return get_sub_index_by_maximum_throughput(subs_slot_CC, sub_with_full_buffer)
        
        case 'Proportional fair':
            return get_sub_index_by_proportional_fair(subs_slot_CC, sub_R_mean_list, sub_with_full_buffer)
    return 0



def get_mean_buffer_score(subs, p, algorithm = 'Maximum Throughput'):
    '''Возвращает оценку среднего объема буффера'''
    V           = const_var.get("V")                    # Объем передоваемого сообщения
    Nrb         = const_var.get("Nrb")                  # Кол-во ресурсных блоков
    slot_cnt    = const_var.get("slot_cnt")             # Кол-во слотов
    subs_cnt    = len(subs.index)                       # Кол-во пользователей

    # Список генерация пакетов входящего потока
    packs = pd.DataFrame(columns=subs.columns, data=[(np.random.geometric(p, size = slot_cnt) - 1) * V for _ in range(subs_cnt)])
    # packs = pd.DataFrame(columns=subs.columns, data=[np.random.exponential(1/lambd) * V for _ in range(subs_cnt)])
    # print('packs', packs)      # ОТЛАДОЧНАЯ ПЕЧАТЬ

    # Записываем пакеты пришедшие в первом слоте в буффер
    buffer = [packs[0].tolist()]

    # Список для ресурсных блоков слотов
    slot_resource_blocks = [[]]

    # Список средних скоростей
    sub_R_mean_list = [[get_R_mean(slot_resource_blocks, subs, sub_idx, 0) for sub_idx in range(subs_cnt)]]

    cur_sub_idx = 0

    # Цикл по слотам
    for slot_idx in range(1, slot_cnt):
        # print(slot_resource_blocks[-1])
        # Расчитываем среднюю скорость только для алгоритмаов EB и PF
        if algorithm in ['Equal Blind', 'Proportional fair']:
            # sub_R_mean_list.append(
                # [get_R_mean(slot_resource_blocks, subs, sub_idx, slot_idx) for sub_idx in range(subs_cnt)]
            # )
            sub_R_mean_list.append(
                [get_R_mean_smoothing_filter(slot_resource_blocks, subs, sub_idx, slot_idx, sub_R_mean_list[-1]) for sub_idx in range(subs_cnt)]
            )
        
        # Присок АБ с заполненным буфером
        sub_with_full_buffer = [i % subs_cnt for i in range(cur_sub_idx, cur_sub_idx + subs_cnt) if buffer[-1][i % subs_cnt] != 0]
        cur_sub_idx = (cur_sub_idx + 1) % subs_cnt

        # Расчет нового буффера для текущего слота
        buffer.append(buffer[-1].copy())    # Копируем буфер прошлого слота

        # Если буфер у всех АБ пуст переходим к сл слоту
        if sub_with_full_buffer: 
        
            # Создание списка приоритетов
            subs_priority_idx = get_sub_index(subs[slot_idx].to_list(), algorithm, sub_with_full_buffer, sub_R_mean_list[-1])

            new_slot_res_block = []

            for sub_idx in subs_priority_idx:
                # Кол-во ресурсных блоков необходимых АБ

                sub_res_block_cnt = int(np.ceil(buffer[-1][sub_idx] / subs[slot_idx][sub_idx]))
                # Добавляем необходимое кол-во ресурсных блоков
                new_slot_res_block.extend([sub_idx for _ in range(sub_res_block_cnt)])
                # Проверка на превышеняе кол-ва ресурсных блоков в слоте
                if len(new_slot_res_block) >= Nrb: break

            # Записываем респределенные ресурсные блоки слота в список
            slot_resource_blocks.append(new_slot_res_block[:Nrb])

            # Цикл по абанентам, которым выделены ресурсные блоки в текущем слоте
            for sub_idx in [sub_idx for sub_idx in slot_resource_blocks[-1] if sub_idx != -1]:
                # Уменьшаем буфер на объем передоваемого АБ сообщения
                buffer[-1][sub_idx] = max(0, buffer[-1][sub_idx] - subs[slot_idx][sub_idx])
        else:
            slot_resource_blocks.append([])

        # Увеличение буфера на обем пакетов генерируемых входным потоком
        buffer[-1] = [sub_buf + sub_pac for sub_buf, sub_pac in zip(buffer[-1], packs[slot_idx])]


    # print(packs.sum(axis = 1))
    # plt.plot([sum(slot_buffer) for slot_buffer in buffer])
    # plt.show()


    # Рассчет среднего суммарного буффера слотов
    return np.mean([sum(slot_buffer) for slot_buffer in buffer])
    # return sum([sum(slot_buffer) for slot_buffer in buffer]) / slot_cnt


def start_model():
    '''Функция моделирования работы системы'''

    lambd_start = const_var.get("lambd_start")    # Начальное значение интенсивности 
    lambd_end   = const_var.get("lambd_end")      # Конечное значение интенсивности
    lambd_step  = const_var.get("lambd_step")     # Шаг изменения

    T_rb = const_var.get("T_rb")    # Длительность слота

    # Интенсивность входного потока (Пакетов/сек)
    lambd_list = np.arange(lambd_start, lambd_end, lambd_step)
    # Параметр p геометрического распределения
    p_list     = 1 / (lambd_list * T_rb + 1)     

    # Цикл по кол-ву АБ
    for subs_cnt in const_var.get("subs_list"):
        # Генерация расположения АБ
        subs = subs_generator(subs_cnt)
        # print('subs', subs)   # ОТЛАДОЧНАЯ ПЕЧАТЬ

        # Цикл по алгоритмам
        for alg in const_var.get("algoritm_list"):

            buffer_score = []
            for p, lambd in zip(p_list, lambd_list):
                buffer_score.append(get_mean_buffer_score(subs, p, alg) / 8192)
                print('subs_cnt = ', subs_cnt, ', lambd = ', lambd, ', alg = ', alg, ', buffer_score = ', buffer_score[-1], sep='')
            # Добавляем линию на график
            plt.plot(lambd_list, buffer_score, marker='o', label = str(subs_cnt) + ' ' + alg)

    # Построение графика
    plt.title('Моделирование для ' + str(const_var.get('slot_cnt')) + ' слотов')
    plt.xlabel('Интенсивность входного потока (пакетов/сек)')
    plt.ylabel('Средн. суммарный объём данных в буфере (Кбайт)')
    plt.legend()
    plt.grid()
    plt.show()

    return 0


if __name__ == "__main__":
	start_model()
    
Соседние файлы в папке ЛР3