Сообщество Engee

Анализ модуляций

Автор
avatar-yurevyurev
Notebook

Анализ модуляционных схем

В данном исследовании представлен системный подход к реализации и верификации тестовых моделей приёмо-передающих трактов в среде Engee. Работа включает последовательный анализ различных аспектов модуляционных схем, начиная с базовых характеристик и заканчивая спектральными свойствами.

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


Для обеспечения корректной работы с моделями реализована функция run_model, которая автоматизирует процесс загрузки и выполнения моделей. Функция выполняет следующие операции:

  • Формирует полный путь к файлу модели с расширением .engee

  • Проверяет текущее состояние модели в ядре системы

  • При необходимости загружает модель из файла или открывает уже загруженную

  • Выполняет запуск модели с выводом подробной информации о процессе

  • Обеспечивает корректное завершение работы с моделью

  • Возвращает результаты выполнения

Данный подход гарантирует стабильную работу независимо от начального состояния системы.

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
Out[0]:
run_model (generic function with 1 method)

Анализ вероятности битовой ошибки

Проведено сравнительное исследование характеристик четырёх модуляционных схем: BPSK, QPSK, 8-PSK и 16-QAM. Методика исследования включает:

  1. Моделирование в диапазоне отношений сигнал-шум (Eb/No) от 0 до 10 дБ с шагом 2 дБ

  2. Теоретический расчёт характеристик BER с использованием математических моделей для каждой модуляции

  3. Визуализацию результатов в логарифмическом масштабе для наглядного сравнения

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

image.png
In [ ]:
EbNoArr = collect(0:2:10);
Eb_No = 0;
ber_bpsk = zeros(length(EbNoArr));
ber_8psk = zeros(length(EbNoArr));
ber_qpsk = zeros(length(EbNoArr));
ber_16qam = zeros(length(EbNoArr));

for i in 1:length(EbNoArr)
    Eb_No = EbNoArr[i]

    run_model("modulations_1");

    ber_bpsk[i] = collect(BER_BPSK).value[end][1]
    ber_8psk[i] = collect(BER_8PSK).value[end][1]
    ber_qpsk[i] = collect(BER_QPSK).value[end][1]
    ber_16qam[i] = collect(BER_16QAM).value[end][1]
end
Building...
Progress 0%
Progress 5%
Progress 16%
Progress 27%
Progress 39%
Progress 49%
Progress 63%
Progress 74%
Progress 83%
Progress 94%
Progress 100%
Progress 100%
Building...
Progress 0%
Progress 7%
Progress 20%
Progress 31%
Progress 44%
Progress 59%
Progress 71%
Progress 82%
Progress 94%
Progress 100%
Progress 100%
Building...
Progress 0%
Progress 7%
Progress 26%
Progress 41%
Progress 61%
Progress 74%
Progress 84%
Progress 93%
Progress 100%
Progress 100%
Building...
Progress 0%
Progress 5%
Progress 15%
Progress 24%
Progress 31%
Progress 39%
Progress 59%
Progress 69%
Progress 80%
Progress 90%
Progress 100%
Progress 100%
Building...
Progress 0%
Progress 6%
Progress 25%
Progress 43%
Progress 59%
Progress 85%
Progress 100%
Progress 100%
Building...
Progress 0%
Progress 6%
Progress 21%
Progress 33%
Progress 44%
Progress 55%
Progress 71%
Progress 84%
Progress 94%
Progress 100%
Progress 100%
In [ ]:
using SpecialFunctions
colors = Dict(:BPSK => :blue, :QPSK => :red, :PSK8 => :green, :QAM16 => :purple)
function theoretical_ber(EbNo_dB, mod_type)
    EbNo = 10 .^ (EbNo_dB ./ 10)
    if mod_type == :BPSK
        0.5 .* erfc.(sqrt.(EbNo))
    elseif mod_type == :QPSK
        0.5 .* erfc.(sqrt.(EbNo)) 
    elseif mod_type == :PSK8
        (2/3) .* erfc.(sqrt.(3*EbNo) .* sin(π/8))
    elseif mod_type == :QAM16
        (3/8) .* erfc.(sqrt.(2 .* EbNo ./ 5))                    
    end
end
EbNoArr_dense = range(minimum(EbNoArr), maximum(EbNoArr), length=1000)
plot(yscale=:log10, ylims=(1e-6, 1), grid=true, xlabel="Eb/No (dB)", ylabel="BER", title="Теоретический и смоделированный BER")

for mod in [(:BPSK, ber_bpsk), (:QPSK, ber_qpsk), (:PSK8, ber_8psk), (:QAM16, ber_16qam)]
    mod_type, ber_sim = mod
    c = colors[mod_type]
    plot!(EbNoArr_dense, theoretical_ber(EbNoArr_dense, mod_type), line=:solid, color=c, label="$mod_type (теор.)")
    scatter!(EbNoArr, ber_sim, marker=:circle, color=c, label="$mod_type (симул.)", markersize=5)
end
plot!(legend=:bottom)
Out[0]:

Анализ мощностных характеристик и сигнальных созвездий

Исследование расширено за счёт модели с фильтрами Найквиста в приёмо-передающих трактах. Проведён анализ:

  • Мощности сигналов до и после фильтрации для оценки влияния фильтров

  • Сигнальных созвездий демодулированных сигналов для визуальной оценки качества демодуляции

Построены созвездия для всех рассматриваемых модуляционных схем с отображением эталонных точек.
Сама модель представлена ниже.

image.png
In [ ]:
run_model("modulations_2")
Building...
Progress 0%
Progress 5%
Progress 11%
Progress 16%
Progress 22%
Progress 27%
Progress 32%
Progress 71%
Progress 76%
Progress 83%
Progress 88%
Progress 93%
Progress 99%
Progress 100%
Progress 100%
Out[0]:
SimulationResult(
    run_id => 17,
    "16qam_demod" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/16qam_demod")
,
    "Find Delay.delay" => WorkspaceArray{Float64}("modulations_2/Find Delay.delay")
,
    "awgn_bpsk" => WorkspaceArray{Matrix{ComplexF64}}("modulations_2/awgn_bpsk")
,
    "ber_qpsk" => WorkspaceArray{Vector{Float64}}("modulations_2/ber_qpsk")
,
    "16qam" => WorkspaceArray{ComplexF64}("modulations_2/16qam")
,
    "qpsk_demod" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/qpsk_demod")
,
    "8psk_demod" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/8psk_demod")
,
    "8psk" => WorkspaceArray{ComplexF64}("modulations_2/8psk")
,
    "bpsk_demod" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/bpsk_demod")
,
    "qpsk" => WorkspaceArray{ComplexF64}("modulations_2/qpsk")
,
    "ber_bpsk" => WorkspaceArray{Float64}("modulations_2/ber_bpsk")
,
    "ber_8psk" => WorkspaceArray{Vector{Float64}}("modulations_2/ber_8psk")
,
    "awgn_16qam" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/awgn_16qam")
,
    "awgn_qpsk" => WorkspaceArray{Matrix{ComplexF64}}("modulations_2/awgn_qpsk")
,
    "16qam_f" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/16qam_f")
,
    "8psk_f" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/8psk_f")
,
    "bpsk" => WorkspaceArray{ComplexF64}("modulations_2/bpsk")
,
    "qpsk_f" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/qpsk_f")
,
    "bpsk_f" => WorkspaceArray{Vector{ComplexF64}}("modulations_2/bpsk_f")
,
    "awgn_8psk" => WorkspaceArray{Matrix{ComplexF64}}("modulations_2/awgn_8psk")
,
    "ber_16qam" => WorkspaceArray{Vector{Float64}}("modulations_2/ber_16qam")

)
In [ ]:
using Statistics 
println("До фильтра:")
bpsk = collect(simout["modulations_2/bpsk"]).value
power_bpsk = mean(abs2.(x[1]) for x in bpsk)
println("Мощность BPSK: $power_bpsk")

qpsk = collect(simout["modulations_2/qpsk"]).value
power_qpsk = mean(abs2.(x[1]) for x in qpsk)
println("Мощность QPSK: $power_qpsk")

psk8 = collect(simout["modulations_2/8psk"]).value
power_8psk = mean(abs2.(x[1]) for x in psk8)
println("Мощность 8PSK: $power_8psk")

qam16 = collect(simout["modulations_2/16qam"]).value
power_16qam = mean(abs2.(x[1]) for x in qam16)
println("Мощность 16QAM: $power_16qam")

println("После фильтра:")
bpsk = collect(simout["modulations_2/bpsk_f"]).value
power_bpsk = mean(abs2.(x[1]) for x in bpsk)
println("Мощность BPSK: $power_bpsk")

qpsk = collect(simout["modulations_2/qpsk_f"]).value
power_qpsk = mean(abs2.(x[1]) for x in qpsk)
println("Мощность QPSK: $power_qpsk")

psk8 = collect(simout["modulations_2/8psk_f"]).value
power_8psk = mean(abs2.(x[1]) for x in psk8)
println("Мощность 8PSK: $power_8psk")

qam16 = collect(simout["modulations_2/16qam_f"]).value
power_16qam = mean(abs2.(x[1]) for x in qam16)
println("Мощность 16QAM: $power_16qam")
До фильтра:
Мощность BPSK: 1.0
Мощность QPSK: 1.0
Мощность 8PSK: 1.0
Мощность 16QAM: 10.25582944703531
После фильтра:
Мощность BPSK: 0.1409425649351918
Мощность QPSK: 0.17159058274740896
Мощность 8PSK: 0.14019923876567178
Мощность 16QAM: 1.4458938239675563

Выполним построения созведия для каждой из модуляций.

In [ ]:
bpsk = collect(simout["modulations_2/bpsk_demod"]).value
bpsk = [x[1] for x in bpsk]  # Извлекаем первый элемент каждого вектора
plot(title="BPSK")
plot!(bpsk, seriestype=:scatter)
plot!([-1+0im, 1+0im], seriestype=:scatter)
Out[0]:
In [ ]:
qpsk = collect(simout["modulations_2/qpsk_demod"]).value;
qpsk = [x[1] for x in qpsk]  # Извлекаем первый элемент каждого вектора
plot(title="QPSK")
plot!(ComplexF64.(qpsk), seriestype=:scatter)
plot!([0.75+0.75im, 0.75-0.75im, -0.75+0.75im, -0.75-0.75im], seriestype=:scatter)
Out[0]:
In [ ]:
psk8 = collect(simout["modulations_2/8psk_demod"]).value;
psk8 = [x[1] for x in psk8]  # Извлекаем первый элемент каждого вектора
plot(title="8-PSK")
plot!(ComplexF64.(psk8), seriestype=:scatter)
plot!(cis.(2pi*[0:7...]/8), seriestype=:scatter)
Out[0]:
In [ ]:
qam16 = collect(simout["modulations_2/16qam_demod"]).value;
qam16 = [(i...)+0 for i in qam16];
plot(title="16QAM")
plot!(ComplexF64.(qam16), seriestype=:scatter)
plot!([a + b*im for a in -3:2:3, b in -3:2:3][:], seriestype=:scatter)
Out[0]:

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

Для углублённого исследования свойств модулированных сигналов выполнено:

  1. Расчёт спектральной плотности мощности с применением:

    • Оконной функции Хэннинга для уменьшения эффектов спектральной утечки

    • Медианной фильтрации для сглаживания спектральных характеристик

  2. Сравнение с теоретической моделью спектра Найквиста с коэффициентом сглаживания 0.2

  3. Визуализацию спектральных характеристик в частотной области

Реализованная модель демонстрирует возможности анализа многочастотных систем с использованием буферизации данных.

image.png
In [ ]:
using FFTW, DSP, Statistics, SpecialFunctions

function compute_smoothed_spectrum(signal, fs, window_size=20)
    window = hanning(length(signal))
    windowed_signal = signal .* window
    power_spectrum = abs.(fft(windowed_signal)).^2 / (sum(abs2, window) * fs)
    power_spectrum_db = 10*log10.(power_spectrum)
    
    function my_medfilt(signal, window_size)
        half_window = window_size ÷ 2
        smoothed = similar(signal)
        n = length(signal)
        for i in 1:n
            start_idx = max(1, i - half_window)
            end_idx = min(n, i + half_window)
            window_data = signal[start_idx:end_idx]
            smoothed[i] = median(window_data)
        end
        return smoothed
    end
    power_spectrum_db_smoothed = my_medfilt(power_spectrum_db, window_size)
    freqs = fftfreq(length(signal), fs)
    return fftshift(freqs), fftshift(power_spectrum_db_smoothed)
end

function nyquist_spectrum(frequencies, rolloff_factor=0.5, symbol_rate=1.0)
    T = 1.0 / symbol_rate
    f_N = 1.0 / (2 * T)
    spectrum = zeros(length(frequencies))
    for (i, f) in enumerate(frequencies)
        f_abs = abs(f)
        if f_abs <= (1 - rolloff_factor) * f_N
            spectrum[i] = T
        elseif f_abs <= (1 + rolloff_factor) * f_N && f_abs > (1 - rolloff_factor) * f_N
            spectrum[i] = T/2 * (1 + cos(π * T / rolloff_factor * (f_abs - (1 - rolloff_factor) * f_N)))
        else
            spectrum[i] = 0.0
        end
    end
    spectrum_db = 10 * log10.(spectrum .+ 1e-12)
    return spectrum_db
end

fs = 400
window_size = 15
symbol_rate = 50.0
rolloff = 0.2

run_model("modulations_3")

bpsk = collect(simout["modulations_3/bpsk_f"]).value
bpsk = [(i...)+0 for i in bpsk]
qpsk = collect(simout["modulations_3/qpsk_f"]).value
qpsk = [(i...)+0 for i in qpsk]
psk8 = collect(simout["modulations_3/8psk_f"]).value
psk8 = [(i...)+0 for i in psk8]
qam16 = collect(simout["modulations_3/16qam_f"]).value
qam16 = [(i...)+0 for i in qam16]

freqs_bpsk, spectrum_bpsk = compute_smoothed_spectrum(bpsk, fs, window_size)
freqs_qpsk, spectrum_qpsk = compute_smoothed_spectrum(qpsk, fs, window_size)
freqs_psk8, spectrum_psk8 = compute_smoothed_spectrum(psk8, fs, window_size)
freqs_qam16, spectrum_qam16 = compute_smoothed_spectrum(qam16, fs, window_size)
freqs_theoretical = range(-fs/2, fs/2, length=1000)
spectrum_nyquist_02 = nyquist_spectrum(freqs_theoretical, 0.2, symbol_rate)
max_experimental = maximum([maximum(spectrum_bpsk), maximum(spectrum_qpsk), maximum(spectrum_psk8), maximum(spectrum_qam16)])
max_theoretical_02 = maximum(spectrum_nyquist_02)
spectrum_nyquist_02_normalized = spectrum_nyquist_02 .- (max_theoretical_02 - max_experimental)

plot(freqs_bpsk, spectrum_bpsk, label="BPSK", linewidth=2, grid=true)
plot!(freqs_qpsk, spectrum_qpsk, label="QPSK", linewidth=2)
plot!(freqs_psk8, spectrum_psk8, label="8-PSK", linewidth=2)
plot!(freqs_qam16, spectrum_qam16, label="16-QAM", linewidth=2)
plot!(freqs_theoretical, spectrum_nyquist_02_normalized, label="Найквист α=0.2", linewidth=3, linestyle=:dash, color=:red)
title!("Энергетический спектр модулированных сигналов (fs = $fs Гц)\nМедианный фильтр с окном $window_size")
xlabel!("Частота, Гц")
ylabel!("Спектральная плотность мощности, дБ/Гц")
xlims!(-fs/2, fs/2)
Building...
Progress 0%
Progress 6%
Progress 23%
Progress 32%
Progress 40%
Progress 49%
Progress 59%
Progress 68%
Progress 78%
Progress 88%
Progress 98%
Progress 100%
Progress 100%
Out[0]:

Полученные результаты позволяют провести комплексный анализ модуляционных схем и обосновать выбор оптимальной модуляции для конкретных условий эксплуатации систем связи.

Вывод.


На основе комплексного анализа характеристик модуляционных схем можно сделать следующие выводы.

Параметр BPSK QPSK 8-PSK 16-QAM
Эффективность 1 б/с/Гц 2 б/с/Гц 3 б/с/Гц 4 б/с/Гц
Требуемый Eb/No для BER=10⁻³ ~7 дБ ~7 дБ ~11 дБ ~15 дБ
Сложность демодуляции Низкая Низкая Средняя Высокая

Лучший выбор для различных сценариев:

  1. Для максимальной помехоустойчивости → BPSK
  2. Оптимальный компромисс → QPSK ⭐
  3. При ограниченной полосе и хорошем SNR → 8-PSK
  4. Для максимальной скорости в идеальных условиях → 16-QAM

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