Скачиваний:
0
Добавлен:
04.05.2025
Размер:
1.63 Mб
Скачать

Выводы:

В ходе выполнения лабораторной работы были получены навыки моделирования стандартных сценариев работы телекоммуникационных систем с топологией типа «звезда», изучены свойства алгоритмов планирования ресурсов нисходящего кадра Maximum Throughput, Proportion Fair и Equal Blind в подобных системах. Изучены стратегии распределения ресурсных блоков в централизованной с ети со случайным трафиком.

Было проанализировано влияние интенсивности трафика на заполнение буферов при различном количестве абонентов (2, 4, 16 и 32):

Для 2 абонентов критическое значение λ составляет примерно 1.0, при превышении которого объем данных в буфере резко увеличивается, достигая ~8.7×10⁸ бит при λ=2.5.

Для 4 абонентов пороговое значение снижается до λ≈0.5, а максимальный объем буфера достигает ~1.5×10⁹ бит при λ=1.5.

При 16 абонентах критический порог падает до λ≈0.1, с максимальным размером буфера ~1.0×10⁹ бит при λ=0.3.

Для 32 абонентов пороговое значение составляет всего λ≈0.05, при этом размер буфера возрастает до ~3.0×10⁹ бит при λ=0.3.

Для всех конфигураций алгоритм Max Throughput демонстрирует наименьший объем данных в буфере, Equal Blind — наибольший, а Proportional Fair занимает промежуточное положение, особенно заметное при высокой нагрузке.

Приложение

Листинг 4 - Программная реализация моделирования

import numpy as np

import matplotlib.pyplot as plt

from scipy.stats import poisson, norm

import asyncio

import time

import nest_asyncio

# Параметры

R = 2500                     # радиус зоны покрытия (м)

Ptx = 10                     # мощность передатчика (Вт)

F0 = 900                     # несущая частота (МГц)

KN = 2                       # коэффициент потерь

N_RB = 25                    # количество ресурсных блоков

packet_size = 8*1024         # размер пакета в битах

tau_RB = 0.5*10**(-3)        # длительность слота (0.5 мс)

delta_f_RB = 180*10**3       # полоса частот

simulation_slots = 10**5     # число слотов

N2_lambda = [0, 1, 1.5, 2.0, 2.5]

N4_lambda = [0, 0.5, 0.75, 1.0, 1.5]

N16_lambda = [0, 0.1, 0.15, 0.2, 0.3]

N32_lambda = [0, 0.05, 0.1, 0.2, 0.3]

N_users_values = [2, 4, 16, 32]  # число пользователей, shape: (4,)

# Параметры для модели Окамура-Хата

Hbs = 40                     # Высота БС (м)

Hrx = 1.7                    # Высота абонента (м)

S = 0                        # Параметр затенения

K = 1.38 * 10**(-23)         # Постоянная Больцмана

T = 300                      # Абсолютная температура (К)

Pn = K * T * delta_f_RB * KN # Мощность шума

# Векторизованная функция для расчета потерь по модели Окамура-Хата

def okumura_hata_vectorized(d, f0, hbs, hrx, s):

    # d: distances - shape: (N_users,) - расстояния от БС до каждого пользователя

    # Возвращает: L_db - shape: (N_users,) - потери сигнала в дБ для каждого пользователя

   

    # Преобразуем расстояние в километры

    d_km = d / 1000  # shape: (N_users,)

   

    # Расчет коэффициента a(hrx)

    a_hrx = (1.1 * np.log10(f0) - 0.7) * hrx - (1.56 * np.log10(f0) - 0.8)

   

    # Расчет потерь по модели Окамура-Хата

    L_db = 46.3 + 33.9 * np.log10(f0) - 13.82 * np.log10(hbs) - a_hrx + (44.9 - 6.55 * np.log10(hrx)) * np.log10(d_km) + s  # shape: (N_users,)

   

    return L_db

# Векторизованная функция для расчета мощности принятого сигнала

def calculate_received_power_vectorized(ptx, L_db):

    # L_db: shape (N_users, N_RB) - затухание сигнала в дБ

    # Возвращает: Prx - shape (N_users, N_RB) - мощность принятого сигнала в ваттах

   

    # L_db должен быть уже вычислен и передан

    Prx = ptx / (10 ** (L_db / 10))  # Принятая мощность в ваттах, shape: (N_users, N_RB)

    return Prx

# Векторизованная функция для расчета SNR

def calculate_snr_vectorized(ptx, L_db):

    # L_db: shape (N_users, N_RB) - затухание сигнала в дБ

    # Возвращает: snr - shape (N_users, N_RB) - отношение сигнал/шум

   

    Prx = calculate_received_power_vectorized(ptx, L_db)  # shape: (N_users, N_RB)

    snr = Prx / Pn  # shape: (N_users, N_RB)

    return snr

# Векторизованная функция для расчета пропускной способности (скорости)

def calculate_data_rate_vectorized(snr, DF):

    # snr: shape (N_users, N_RB) - отношение сигнал/шум

    # DF: полоса частот

    # Возвращает: shape (N_users, N_RB) - пропускная способность в бит/с

    return DF * np.log2(1 + snr)  # shape: (N_users, N_RB)

# Функция для размещения абонентов внутри окружности

def place_users(N, R):

    # N: число пользователей

    # R: радиус зоны покрытия

    # Возвращает: radii - shape: (N,) - расстояния от БС до каждого пользователя

   

    radii = np.sqrt(np.random.uniform(0, R**2, N))  # shape: (N,)

    return radii

def model(N_users, lambda_val):

    # N_users: число пользователей

    # lambda_val: интенсивность поступления пакетов

    # Возвращает: кортеж из трех значений - средние размеры буферов для трех алгоритмов

   

    distances = place_users(N_users, R)  # shape: (N_users,) - расстояния от БС до каждого пользователя

   

    # Буферы для каждого алгоритма - словарь с тремя ключами, каждый содержит numpy array

    # shape каждого массива: (N_users,) - размер буфера для каждого пользователя в битах

    buffers = {'Equal Blind': np.zeros(N_users),  

               'Maximum Throughput': np.zeros(N_users),

               'Proportional Fair': np.zeros(N_users)}

   

    # Начальные средние скорости - словарь с тремя ключами, каждый содержит numpy array

    # shape каждого массива: (N_users,) - средняя скорость передачи в бит/с для каждого пользователя

    R_avg = {'Equal Blind': np.ones(N_users)*1e-6,

             'Maximum Throughput': np.ones(N_users)*1e-6,

             'Proportional Fair': np.ones(N_users)*1e-6}

   

    # История буферов - словарь с тремя ключами, каждый содержит пустой список

    # Будет наполняться суммарным размером буфера на каждой итерации

    hist = {'Equal Blind': [], 'Max Throughput': [], 'Proportional Fair': []}

   

    # Предварительно вычисляем базовые потери для всех пользователей

    # shape: (N_users, 1) - базовое затухание сигнала в дБ для каждого пользователя

    Li_base = okumura_hata_vectorized(distances, F0, Hbs, Hrx, S).reshape(N_users, 1)

   

    # Предварительно выделяем память для массивов, которые будут многократно использоваться

    Li = np.zeros((N_users, N_RB))     # shape: (N_users, N_RB) - затухание сигнала для каждого пользователя и ресурсного блока

    C = np.zeros((N_users, N_RB))      # shape: (N_users, N_RB) - пропускная способность для каждого пользователя и ресурсного блока

    V = np.zeros((N_users, N_RB))      # shape: (N_users, N_RB) - максимальный объем данных в битах, который можно передать

    p_ij_k = np.zeros((N_users, N_RB)) # shape: (N_users, N_RB) - приоритеты для алгоритмов планирования

   

    for k in range(simulation_slots):

        # Генерируем случайные затухания (векторизованно)

        # x_ij_k: shape: (N_users, N_RB) - случайная компонента затухания

        x_ij_k = norm.rvs(0, 1, size=(N_users, N_RB))

        # Li: shape: (N_users, N_RB) - полное затухание сигнала для каждого пользователя и ресурсного блока

        Li = np.tile(np.mean(Li_base), (N_users, N_RB)) + x_ij_k

       

        # Генерируем пакеты для всех пользователей сразу

        # packets: shape: (N_users,) - число пакетов для каждого пользователя

        packets = poisson.rvs(lambda_val, size=N_users)

           

        # Вычисляем SNR и пропускную способность (векторизованно)

        # snr_values: shape: (N_users, N_RB) - SNR для каждого пользователя и ресурсного блока

        snr_values = calculate_snr_vectorized(Ptx, Li)

        # C: shape: (N_users, N_RB) - пропускная способность в бит/с для каждого пользователя и ресурсного блока

        C = calculate_data_rate_vectorized(snr_values, delta_f_RB)

        # V: shape: (N_users, N_RB) - максимальный объем данных в битах, который можно передать за один слот

        V = C * tau_RB

       

        # Распределение ресурсов для каждого алгоритма

        for algorithm in buffers:

            # Расчет приоритетов (векторизованно)

            if algorithm == 'Equal Blind':

                # p_ij_k: shape: (N_users, N_RB) - приоритеты для алгоритма Equal Blind

                p_ij_k = np.ones((N_users, N_RB)) / R_avg[algorithm].reshape(N_users, 1)

            elif algorithm == 'Max Throughput':

                # p_ij_k: shape: (N_users, N_RB) - приоритеты для алгоритма Max Throughput (равны пропускной способности)

                p_ij_k = C.copy()

            elif algorithm == 'Proportional Fair':

                # p_ij_k: shape: (N_users, N_RB) - приоритеты для алгоритма Proportional Fair

                p_ij_k = C / R_avg[algorithm].reshape(N_users, 1)

           

            # Инициализация счетчика для отслеживания объема данных

            # transmit_volume: shape: (N_users,) - объем переданных данных для каждого пользователя

            transmit_volume = np.zeros(N_users)

           

            # Добавляем пакеты в буферы (размер каждого пакета в битах умножается на число пакетов)

            buffers[algorithm] += packets * packet_size  # shape: (N_users,)

            # Определяем активных пользователей (тех, у кого буфер не пуст)

            # active_users: shape: (M,) - индексы активных пользователей, где M <= N_users

            active_users = np.where(buffers[algorithm] > 0)[0]

           

            if len(active_users) > 0:

                # Инициализация массива распределения ресурсных блоков

                # rb_allocation: shape: (N_RB,) - массив, показывающий какому пользователю назначен каждый ресурсный блок

                rb_allocation = np.full(N_RB, -1, dtype=int)

               

                # Вычисляем приоритеты только для активных пользователей

                # active_priorities: shape: (M, N_RB) - приоритеты только для активных пользователей

                active_priorities = p_ij_k[active_users]

               

                # Распределяем ресурсные блоки одним проходом

                for rb_index in range(N_RB):

                    if len(active_users) == 0:

                        break

                   

                    # Находим индекс максимального приоритета среди активных пользователей

                    # best_user_local_idx: индекс пользователя с максимальным приоритетом в массиве active_users

                    best_user_local_idx = np.argmax(active_priorities[:, rb_index])

                    # selected_user_id: фактический индекс выбранного пользователя

                    selected_user_id = active_users[best_user_local_idx]

                   

                    # Назначаем ресурсный блок выбранному пользователю

                    rb_allocation[rb_index] = selected_user_id

                   

                    # Вычисляем объем данных для передачи (минимум из пропускной способности и размера буфера)

                    # data_to_transmit: объем данных для передачи в битах

                    data_to_transmit = min(V[selected_user_id, rb_index], buffers[algorithm][selected_user_id])

                   

                    # Обновляем буфер и счетчик переданных данных

                    buffers[algorithm][selected_user_id] -= data_to_transmit

                    transmit_volume[selected_user_id] += data_to_transmit

                   

                    # Если буфер пуст, удаляем пользователя из активных

                    if buffers[algorithm][selected_user_id] <= 0:

                        # Находим индекс в массиве active_users

                        # remove_idx: индекс пользователя в массиве active_users, которого нужно удалить

                        remove_idx = np.where(active_users == selected_user_id)[0][0]

                        # Удаляем пользователя и его приоритеты из соответствующих массивов

                        active_users = np.delete(active_users, remove_idx)  # shape: (M-1,)

                        active_priorities = np.delete(active_priorities, remove_idx, axis=0)  # shape: (M-1, N_RB)

           

            # Сглаживающий фильтр для обновления средних скоростей

            y = 1.

            beta = tau_RB / y  # коэффициент сглаживания

            # Обновляем средние скорости для алгоритма, shape: (N_users,)

            R_avg[algorithm] = (1-beta)*R_avg[algorithm] + beta*(transmit_volume/tau_RB)

           

            # Добавляем текущий суммарный размер буфера в историю

            hist[algorithm].append(np.sum(buffers[algorithm]))  # сумма всех буферов

   

    # Возвращаем средние значения истории буферов для трех алгоритмов

    # Каждое значение - среднее по всем итерациям для данного алгоритма

    return (np.mean(hist['Equal Blind']),

            np.mean(hist['Maximum Throughput']),

            np.mean(hist['Proportional Fair']))

async def model_async(N, lam):

    """Асинхронная обертка для функции model"""

    try:

        if lam == 0:

            print(f"Пропуск моделирования для N={N}, λ={lam} (всегда нули)")

            return (0, 0, 0), N, lam

       

        # Передаем выполнение основной функции, которая может занять много времени

        result = await asyncio.to_thread(model, N, lam)

        print(f"Выполнено моделирование для N={N}, λ={lam}")

        return result, N, lam

    except Exception as e:

        print(f"Ошибка при N={N}, λ={lam}: {str(e)}")

        return (0, 0, 0), N, lam

async def main():

    start_time = time.time()

    results = {N: {'Equal Blind': [], 'Maximum Throughput': [], 'Proportional Fair': []} for N in N_users_values}

   

    # Используем фиксированное начальное значение для воспроизводимости результатов

    np.random.seed(42)

   

    # Создаем список задач для асинхронного выполнения

    tasks = []

    for N in N_users_values:

        if N == 2:

            lambda_values = N2_lambda

        elif N == 4:

            lambda_values = N4_lambda

        elif N == 16:

            lambda_values = N16_lambda

        elif N == 32:

            lambda_values = N32_lambda

        for lam in lambda_values:

            print(f"Запуск моделирования: N={N}, λ={lam}")

            tasks.append(model_async(N, lam))

   

    # Запускаем все задачи параллельно и ждем результаты

    completed_tasks = await asyncio.gather(*tasks)

   

    # Обрабатываем результаты

    for result, N, lam in completed_tasks:

        eb, mt, pf = result

        if N == 2:

            lambda_values = N2_lambda

        elif N == 4:

            lambda_values = N4_lambda

        elif N == 16:

            lambda_values = N16_lambda

        elif N == 32:

            lambda_values = N32_lambda

        lambda_index = list(lambda_values).index(lam)

        results[N]['Equal Blind'].insert(lambda_index, eb)

        results[N]['Maximum Throughput'].insert(lambda_index, mt)

        results[N]['Proportional Fair'].insert(lambda_index, pf)

   

    end_time = time.time()

    print(f"Общее время выполнения: {end_time - start_time} секунд")

    print(f"Моделирование успешно завершено для всех конфигураций!")

   

    return results

nest_asyncio.apply()

results_optimized = asyncio.run(main())

results_plot = results_optimized

# Визуализация результатов

plt.figure(figsize=(16, 10))

for i, N in enumerate(N_users_values, 1):

    plt.subplot(2, 2, i)

    if N == 2:

        lambda_values = N2_lambda

    elif N == 4:

        lambda_values = N4_lambda

    elif N == 16:

        lambda_values = N16_lambda

    elif N == 32:

        lambda_values = N32_lambda

    plt.plot(lambda_values, results_plot[N]['Equal Blind'], linewidth=1, marker='o', linestyle='-', markersize=3, alpha=0.7, label='Equal Blind')

    plt.plot(lambda_values, results_plot[N]['Maximum Throughput'], linewidth=1, marker='s', linestyle='--', markersize=3, alpha=0.7, label='Maximum Throughput')

    plt.plot(lambda_values, results_plot[N]['Proportional Fair'], linewidth=1, marker='d', linestyle=':', markersize=3, alpha=0.7, label='Proportional Fair')

   

    plt.title(f'N={N} абонентов', fontsize=14)

    plt.xlabel('λ', fontsize=12)

    plt.ylabel('Средний суммарный объем данных в буфере (бит)', fontsize=12)

    plt.legend(fontsize=10)

    plt.grid(True, linestyle='--', alpha=0.7)

    plt.tick_params(axis='both', which='major', labelsize=10)

plt.tight_layout(pad=3.0)

plt.suptitle('Зависимость среднего суммарного объема данных в буфере от λ', fontsize=16,  y=0.98)

plt.savefig('buffer_results.png', dpi=300, bbox_inches='tight')

plt.show()

Соседние файлы в предмете Моделирование систем распределения ресурсов