AnyMath 文档
Notebook

调制电路分析

本研究提出了一种在AnyMath环境中实现和验证收发信路测试模型的系统化方法。 本文对调制方案的各个方面进行了系统分析,内容涵盖从基本特性到频谱分析等各个方面。

模型管理辅助功能


为确保模型的正确运行,已实现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. 在 0 至 10 dB 的信噪比(Eb/No)范围内,以 2 dB 为步长进行 仿真

  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 Hz)\n具有窗口 $window_size 的中位数滤波器")
xlabel!("频率,赫兹")
ylabel!("功率谱密度,dB/Hz")
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 bps/Hz 2 bps/Hz 3 bps/Hz 4 bps/Hz
当误码率=10⁻³时所需的Eb/No ~7 dB ~7 dB ~11 dB ~15 dB
解调复杂度 中等

各种场景下的最佳选择:

  1. 追求最大抗噪性能 → BPSK
  2. 最佳折中方案 → QPSK ⭐
  3. 带宽受限且信噪比(SNR)良好时 → 8-PSK
  4. 在理想条件下追求最高速率 → 16-QAM

综上所述,对于大多数实际通信系统而言,QPSK 是最均衡且实用的选择,它在抗噪性能、频谱效率和实现简便性之间实现了最佳平衡。 BPSK应用于对可靠性有极端要求的系统,而更高阶的调制方式仅应在通信信道质量得到保证的情况下使用。