Сообщество Engee

CDMA-система

Автор
avatar-yurevyurev
Notebook

Моделирование CDMA-системы с QPSK-модуляцией

В современных системах беспроводной связи технология множественного доступа с кодовым разделением каналов CDMA (Code Division Multiple Access) остается фундаментальным методом обеспечения одновременной передачи данных для нескольких пользователей в общей частотной полосе. Ключевым аспектом проектирования таких систем является обеспечение ортогональности каналов и устойчивость к шумам, что требует тщательного моделирования на этапе разработки.

Ключевые принципы CDMA:

Принцип Описание
Расширение спектра Каждый бит данных умножается на высокочастотную кодовую последовательность (chip sequence), что «расширяет» сигнал по частоте
Ортогональность кодов Коды разных каналов выбираются так, чтобы их взаимная корреляция была близка к нулю (например, коды Уолша), что минимизирует интерференцию
Совместное использование частоты Все пользователи работают на одной несущей частоте одновременно, различаясь только кодом
Связывание (Despreading) На приёмной стороне сигнал умножается на тот же код, что восстанавливает исходные данные и подавляет сигналы других пользователей

Преимущества CDMA:

  • Устойчивость к узкополосным помехам и замираниям
  • Возможность мягкого хандовера (soft handoff) в сотовых сетях
  • Гибкое управление ёмкостью системы
  • Повышенная безопасность передачи за счёт псевдослучайных кодов

Применение:

  • Сотовые сети 2G/3G (IS-95, CDMA2000, WCDMA)
  • Системы глобального позиционирования (GPS, ГЛОНАСС)
  • Спутниковая связь
  • Военные системы защищённой связи

В нашей модели CDMA реализована через комбинацию QPSK-модуляции, кодов Уолша (для разделения пилотного и пользовательского каналов) и PN-последовательностей (для дополнительного расширения спектра), что соответствует классической архитектуре систем с прямым расширением спектра (DSSS-CDMA).

схема.png

Модель состоит из двух пользовательских подсистем (Pilot_Channel и User1_Traffic_Channel), которые формируют сигналы пилот-канала и пользовательского трафика. Сигналы подвергаются квадратурной фазовой манипуляции (QPSK), расширяются уникальными кодовыми последовательностями и суммируются перед передачей через шумовой канал. На стороне приемника производится обратная операция свертки и демодуляция для восстановления исходных данных.

image.png

Интеграция с Engee позволяет гибко управлять параметрами сигналов: кодовые последовательности, рассчитанные в скрипте, загружаются в модель через блоки Signal From Workspace, что обеспечивает высокую точность соответствия математического описания и симуляции.

In [ ]:
Pkg.add("JLD2")
using JLD2, Statistics, DataFrames
   Resolving package versions...
  No Changes to `~/.project/Project.toml`
  No Changes to `~/.project/Manifest.toml`

Код ниже выполняет начальную загрузку и визуальную проверку данных для моделирования.

In [ ]:
A = load("data.jld2", "A")
Size_A = length(A)
println("Size A: $(Size_A)")
plot(A[1:300])
Size A: 24000
Out[0]:

Функция pn_gen(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)

  • Реализует генератор псевдослучайной последовательности (PN) на основе регистра сдвига с линейной обратной связью (LFSR).
  • Принимает начальное состояние регистра, вектор показателей образующего полинома и требуемую длину выходной последовательности.
  • Определяет длину регистра N и создает его копию для работы.
  • Преобразует показатели полинома в позиции отводов (taps): если показатель равен 0 — добавляет отвод на последнюю позицию N, иначе — на позицию N-e.
  • Выполняет генерацию последовательности заданной длины: на каждом шаге сохраняет последний бит регистра как выходной, вычисляет бит обратной связи как XOR всех отводов и выполняет сдвиг регистра вправо с помещением бита обратной связи в начало.
  • Возвращает массив целых чисел 0 и 1 (совместимый с форматом MATLAB).

Функция pn_gen_walsh(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)

  • Вызывает pn_gen для получения базовой бинарной последовательности.
  • Выполняет преобразование формата: каждый 0 заменяет на 1, каждую 1 заменяет на -1.
  • Возвращает последовательность в формате +1/-1, готовую для использования в модуляции и умножении на коды Уолша.

Функция walsh_hadamard(n::Int, size::Int)

  • Генерирует ортогональные коды Уолша-Адамара заданной длины.
  • Начинает с базовой матрицы Адамара размером 4×4.
  • Выполняет два итеративных расширения по правилу [H H; H -H], формируя матрицу 16×16.
  • Извлекает строку с индексом n+1 (нумерация каналов с 0).
  • Повторяет (повтор) полученную строку необходимое количество раз для достижения требуемой длины size.
  • Возвращает последовательность из +1/-1, ортогональную другим строкам матрицы Адамара.
In [ ]:
function pn_gen(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)
    N = length(init_state)
    reg = copy(init_state)
    taps = Int[]
    for e in poly_exp
        if e == 0
            push!(taps, N)
        elseif e != N
            push!(taps, N - e)
        end
    end
    outputs = Bool[]
    for _ in 1:n
        push!(outputs, reg[end] == 1)
        fb = 0
        for t in taps
            fb = xor(fb, reg[t])
        end
        reg = [fb; reg[1:end-1]]
    end
    return Int.(outputs)
end

function pn_gen_walsh(init_state::Vector{Int}, poly_exp::Vector{Int}, n::Int)
    raw = pn_gen(init_state, poly_exp, n)
    return [x == 0 ? 1 : -1 for x in raw]
end

function walsh_hadamard(n::Int, size::Int)
    H = [1 1 1 1; 1 -1 1 -1; 1 1 -1 -1; 1 -1 -1 1]
    for i in 1:2
        H = [H H; H -H]
    end
    code = H[n+1, :]
    return repeat(code, inner=div(size, 16))
end
Out[0]:
walsh_hadamard (generic function with 1 method)

Ниже представлен код формирования PN-кодов.

PN-код генерируется отдельно для двух ветвей сигнала — синфазной (I) и квадратурной (Q). Для каждой ветви используется специальный генератор, формирующий последовательность случайных значений (+1 или −1), основанную на определенных параметрах регистра сдвига и полинома обратной связи.

Коды Уолша создаются для разделения каналов внутри системы. Пилотный канал получает код, состоящий из одних единиц, а первый пользовательский канал — чередующийся код. Эти коды позволяют разделить сигналы разных пользователей и помогают приемнику выделять нужный сигнал среди множества передающихся одновременно.

Для каждого канала формируется итоговый сигнал путем перемножения PN-кода и соответствующего кода Уолша. Это обеспечивает разделение сигналов различных пользователей и помогает эффективно передавать данные параллельно. Затем выполняется проверка характеристик полученных сигналов. Подтверждается, что они хорошо сбалансированы (среднее близко к нулю, стандартное отклонение равно 1), что важно для качественной передачи данных.

In [ ]:
pn_I = pn_gen_walsh([1; zeros(Int, 14)], [15, 13, 9, 8, 7, 5, 1], Size_A)
pn_Q = pn_gen_walsh([1; zeros(Int, 14)], [15, 12, 11, 10, 6, 5, 4, 3, 1], Size_A)
walsh_pilot = walsh_hadamard(0, Size_A)  # [1,1,1,1...]
walsh_user1 = walsh_hadamard(1, Size_A)  # [1,-1,1,-1...]

pilot_I = pn_I .* walsh_pilot
pilot_Q = pn_Q .* walsh_pilot
user1_I = pn_I .* walsh_user1
user1_Q = pn_Q .* walsh_user1

code_list = [A, pilot_I, pilot_Q, user1_I, user1_Q]
code_names = ["A (engee)", "Pilot I", "Pilot Q", "User1 I", "User1 Q"]

df_stats = DataFrame(
    Код = code_names,
    Длина = [length(c) for c in code_list],
    Среднее = [round(mean(c), digits=4) for c in code_list],
    Std = [round(std(c), digits=4) for c in code_list],
    Мин = [minimum(c) for c in code_list],
    Макс = [maximum(c) for c in code_list],
    Процент_1 = [round(count(==(1), c)/length(c)*100, digits=2) for c in code_list],
    Процент_minus1 = [round(count(==(-1), c)/length(c)*100, digits=2) for c in code_list]
)
println(df_stats)
5×8 DataFrame
 Row │ Код        Длина  Среднее  Std      Мин    Макс   Процент_1  Процент_minus1
     │ String     Int64  Float64  Float64  Int64  Int64  Float64    Float64
─────┼─────────────────────────────────────────────────────────────────────────────
   1 │ A (engee)  24000   0.5012   0.5         0      1      50.12            0.0
   2 │ Pilot I    24000   0.0028   1.0        -1      1      50.14           49.86
   3 │ Pilot Q    24000   0.0196   0.9998     -1      1      50.98           49.02
   4 │ User1 I    24000   0.0068   1.0        -1      1      50.34           49.66
   5 │ User1 Q    24000   0.0006   1.0        -1      1      50.03           49.97

Анализ статистической таблицы показывает следующие характеристики сигналов:

  • Исходный сигнал "A" является униполярным с равным распределением нулей и единиц. Стандартное отклонение соответствует ожиданиям для бинарного сигнала с равной вероятностью символов.
  • Сигналы пилотного канала (Pilot I и Pilot Q) имеют средние значения близкие к нулю, что указывает на хорошую балансировку и отсутствие постоянной составляющей. Стандартное отклонение равно единице, подтверждающее нормированную мощность сигнала. Процентное распределение единиц и минус единиц близко к 50/50, обеспечивая сбалансированность последовательностей.
  • Пользовательские каналы (User1 I и User1 Q) также демонстрируют близкие к нулю средние значения и стандартное отклонение, равное единице. Распределение положительных и отрицательных единиц максимально сбалансировано, особенно у User1 Q.

Общие выводы подтверждают, что все канальные сигналы обладают оптимальными характеристиками для передачи: нулевая постоянная составляющая, нормированная мощность и равновероятность символов. Небольшие отклонения от идеала связаны с конечностью последовательности и статистическими колебаниями.

Код ниже вычисляет корреляцию между парами каналов, в нём создается список corr_data, содержащий пары каналов и соответствующие коэффициенты корреляции. Коэффициент рассчитывается путем поэлементного перемножения соответствующих элементов двух каналов, суммирования результата и нормализации делением на длину последовательности. Затем формируется таблица df_corr с двумя столбцами: названия пар каналов и численные значения корреляций. Таблица выводится на экран командой println.

In [ ]:
corr_data = [
    ("Pilot I × User1 I", round(sum(pilot_I .* user1_I)/length(pilot_I), digits=4)),
    ("Pilot Q × User1 Q", round(sum(pilot_Q .* user1_Q)/length(pilot_Q), digits=4))
]

df_corr = DataFrame(
    Пара = [x[1] for x in corr_data],
    Корреляция = [x[2] for x in corr_data]
)
println(df_corr)
2×2 DataFrame
 Row │ Пара               Корреляция
     │ String             Float64
─────┼───────────────────────────────
   1 │ Pilot I × User1 I         0.0
   2 │ Pilot Q × User1 Q         0.0

Нулевая корреляция подтверждает ортогональность кодов Уолша, отсутствие взаимных помех и правильное формирование канальных сигналов в системе CDMA, теперь запустим нашу модель и убедимся в корректности выбранных значений.

image.png
In [ ]:
function run_model( name_model)
    Path = (@__DIR__) * "/" * name_model * ".engee"
    if name_model in [m.name for m in engee.get_all_models()] 
        model = engee.open( name_model ) 
        model_output = engee.run( model, verbose=true ); 
    else
        model = engee.load( Path, force=true ) 
        model_output = engee.run( model, verbose=true );
        engee.close( name_model, force=true );
    end
    sleep(0.1)
    return model_output
end
run_model("CDMA_MAI")
BER = collect(simout["CDMA_MAI/Error Rate Calculation.1"]).value[end]
println("Коэффициент битовых ошибок (BER): $(BER[1])")
println("Количество ошибок: $(BER[2])")
println("Общее количество бит: $(BER[3])")
Building...
Progress 0%
Progress 23%
Progress 100%
Progress 100%
Коэффициент битовых ошибок (BER): 0.0
Количество ошибок: 0.0
Общее количество бит: 1456.0

Вывод

При уровне отношения сигнал-шум SNR = 10 дБ, была достигнута нулевая вероятность битовой ошибки (BER = 0). Это демонстрирует правильную работу всех компонентов модели CDMA: генерация псевдошумовых последовательностей, создание ортогональных канальных кодов, модуляцию QPSK с фазовым сдвигом /4 и последующую демодуляцию.

Полученный результат корреляционного анализа подтверждает полное отсутствие перекрестных помех между каналами благодаря идеальной ортогональности используемых кодов Уолша. Таким образом, проведённое моделирование успешно продемонстрировало правильность функционирования системы CDMA и её способность обеспечивать надёжную передачу данных без потерь на заданной длине последовательности.