AnyMath 文档
Notebook

使用PCM和PWM进行信号转换

在这个例子中,让我们比较AnyMath模型中的脉冲编码调制(PCM)和脉冲宽度调制(PWM)。 让我们测量量化和编码误差,估计滤波器延迟,并检查平均PWM值对输入信号的依赖性。

采样和量化

采样时,我们取样 . 要恢复带限信号,采样频率必须超过频谱上限频率的两倍。

量化器以增量对样本进行舍入 :

对于均匀分布的误差,其均方根值估计为 .

编码和解码

为 位和范围 步等于 . 签名代码从 以前 ,并且解码器将代码乘以 . 可恢复水平上限 — .

信号功率与量化误差功率之比:

脉冲宽度调制

PWM信号在0和1之间切换。 其在周期内的平均值等于填充因子:

用离散的步骤 填充增量变化 .

模型描述

模型 pcm_pwm_analysis 如下图所示。

pcm_pwm_analysis_model.png

该模型包含PCM和PWM分支。 在PCM中依次执行滤波、采样、量化、编码和解码。

错误信号 正在比较的是什么
quant_error 量化器的输入和输出
pcm_error 量化和解码信号
总计-错误 量化器输入和解码信号

我们分别评估滤波器的延迟与编码错误。

让我们准备分析函数。

In [ ]:
using Main: engee, plot, plot!, histogram, hline!, savefig, Animation, frame, gif
using Statistics

让我们设置模型参数并计算量化步长和预期SQNR。

In [ ]:
# Pcm_pwm_analysis模型块的参数
A      = 0.5         # 正弦波的振幅
Ω      = 1.0         # 正弦波频率,rad/s
ωc     = 30.0        # 模拟滤波器设计滤波器的截止频率,rad/s
N      = 8           # 过滤顺序
Ts     = 0.01        # 零阶保持块的采样时间,用
q_set  = 0.0078125   # 量化器块的量化间隔
V      = 1.0         # 均匀编码器和均匀解码器块的峰值
B      = 8           # 均匀编码器和均匀解码器块的位
T_pwm  = 0.05        # 周期的PWM块,与
Ts_pwm = 0.001       # Pwm块的采样时间,与

q = 2V / 2^B
sqnr_theory = 10 * log10((A^2 / 2) / (q^2 / 12))

println("Шаг кодера q = 2V/2^B: ", q, ";匹配量化器间隔: ", q == q_set)
println("Предел ошибки квантования q/2: ", q / 2)
println("СКЗ ошибки при равномерном распределении q/√12: ", round(q / sqrt(12), sigdigits = 4))
println("1.0级代码无饱和: ", round(Int, 1.0 / q), ";最大的代码: ", 2^(B - 1) - 1)
println("幅度正弦波的SQNR估计 ", A, ": ", round(sqnr_theory, digits = 1), " 分贝")
println("Шаг коэффициента заполнения PWM T_pwm/T: ", Ts_pwm / T_pwm)
Шаг кодера q = 2V/2^B: 0.0078125; совпадает с интервалом Quantizer: true
Предел ошибки квантования q/2: 0.00390625
СКЗ ошибки при равномерном распределении q/√12: 0.002255
Код уровня 1.0 без насыщения: 128; наибольший код: 127
Оценка SQNR для синусоиды амплитуды 0.5: 43.9 дБ
Шаг коэффициента заполнения PWM T_pwm/T: 0.02

最大签名代码为127,因此我们无法准确表示1.0级别。 对于所使用的信号幅度,预期的SQNR约为44dB。

巴特沃斯滤波器的传递函数是从其极点获得的:

让我们通过模数和相位来估计信号频率处的增益和延迟 .

In [ ]:
# 模拟巴特沃斯滤波器的极点和频率响应
poles = [ωc * exp(im * π * (2k + N - 1) / (2N)) for k in 1:N]
H(s) = prod(-p / (s - p) for p in poles)

H_sig = H(im * Ω)
τ_filter = -angle(H_sig) / Ω

println("模块/H(jΩ)/在信号频率: ", round(abs(H_sig), digits = 6))
println("单个滤波器的相位延迟: ", round(τ_filter, digits = 4), " 与")
println("零频率下的群延迟: ", round(1 / (ωc * sin(π / (2N))), digits = 4), " 与")
Модуль |H(jΩ)| на частоте сигнала: 1.0
Фазовая задержка одного фильтра: 0.1709 с
Групповая задержка на нулевой частоте: 0.1709 с

一个滤波器在接近单位的增益下提供约0.17s的延迟。 两个连续的滤波器大致加倍延迟。

从示例文件夹下载模型并运行计算。

In [ ]:
model_name = "pcm_pwm_analysis"
model_file = joinpath(@__DIR__, model_name * ".engee")

model = engee.load(model_file; force = true)
results = engee.run(model);

我们将接收记录的信号并关闭模型。

In [ ]:
x_sine  = collect(results["Sine Wave.1"])
x_zoh   = collect(results["Zero-Order Hold.1"])
x_quant = collect(results["Quantizer.1"])
x_code  = collect(results["Uniform Encoder.1"])
x_dec   = collect(results["Uniform Decoder.1"])
x_add   = collect(results["Add.1"])
x_duty  = collect(results["Analog Filter Design-1.1"])
x_pwm   = collect(results["PWM.1"])

engee.close(model_name; force = true)

signals = ["Sine Wave" => x_sine, "Zero-Order Hold" => x_zoh, "Quantizer" => x_quant,
           "Uniform Encoder" => x_code, "Uniform Decoder" => x_dec, "Add" => x_add,
           "Analog Filter Design-1" => x_duty, "PWM" => x_pwm]

for (name, s) in signals
    println(rpad(name, 24), length(s.time), " 分,t从 ", first(s.time), " 以前 ", last(s.time),
            " с, шаг ≈ ", round(median(diff(s.time)), sigdigits = 3), " с, тип ", eltype(s.value))
end
Sine Wave               20001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.001 с, тип Float64
Zero-Order Hold         2001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.01 с, тип Float64
Quantizer               2001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.01 с, тип Float64
Uniform Encoder         2001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.01 с, тип Int64
Uniform Decoder         2001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.01 с, тип Float64
Add                     2001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.01 с, тип Float64
Analog Filter Design-1  20001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.001 с, тип Float64
PWM                     20001 точек, t от 0.0 до 20.0 с, шаг ≈ 0.001 с, тип Float64

信号记录在不同的时间网格上。 让我们把它们带到一个共同的网格进行比较。

Hold_at函数选择指定时间点之前的最后一个倒计时。 我们用它来比较脚本错误和模型块的输出。

In [ ]:
function hold_at(s, t)
    idx = searchsortedlast.(Ref(s.time), t .+ 1e-6)
    return s.value[clamp.(idx, 1, length(s.time))]
end

rms(x) = sqrt(mean(abs2, x))

t_k = collect(0:Ts:last(x_zoh.time))

u_k   = hold_at(x_zoh, t_k)          # 量化器输入(零阶保持输出)
y_k   = hold_at(x_quant, t_k)        # 量化器输出
c_k   = Int.(hold_at(x_code, t_k))   # 统一编码器代码
d_k   = hold_at(x_dec, t_k)          # 统一解码器输出
add_k = hold_at(x_add, t_k)          # 添加输出

quant_error = y_k .- u_k             # Quantizer − Zero-Order Hold
pcm_error   = y_k .- d_k             # Quantizer − Uniform Decoder
total_error = d_k .- u_k             # Uniform Decoder − Zero-Order Hold

println("采样时刻: ", length(t_k))
println("Pcm_error和Add输出之间的最大差异: ", maximum(abs.(pcm_error .- add_k)))
Моментов дискретизации: 2001
Наибольшее расхождение pcm_error и выхода Add: 0.0

计算出的PCM误差与相应添加块的信号重合。

量化误差

让我们检查量化误差及其RMS值的限制。

In [ ]:
println("量化器量化误差-零阶保持")
println("  最大的模块: ", round(maximum(abs.(quant_error)), sigdigits = 5), " (предел q/2 = ", q / 2, ")")
println("  SKZ的: ", round(rms(quant_error), sigdigits = 5), " (оценка q/√12 = ", round(q / sqrt(12), sigdigits = 5), ")")
println("  平均: ", round(mean(quant_error), sigdigits = 3))
Ошибка квантования Quantizer − Zero-Order Hold
  наибольший модуль: 0.003905 (предел q/2 = 0.00390625)
  СКЗ: 0.0022233 (оценка q/√12 = 0.0022553)
  среднее: -4.57e-5

误差不超过±q/2。 让我们将其分布与q/√12的估计值进行比较。

In [ ]:
histogram(quant_error ./ q;
    bins = range(-0.5, 0.5; length = 21),
    normalize = :pdf,
    label = "quant_error / q",
    xlabel = "步骤q的分数误差",
    ylabel = "密度",
    title = "量化误差的分布",
    size = (900, 400))
hline!([1.0]; label = "均匀分布", linestyle = :dash, linewidth = 2)

直方图不必是均匀的:在20秒的模拟中,缓慢的输入信号仅通过几个周期。

我们会发现编码错误并检查顶层1.0发生了什么。

In [ ]:
nz = findall(abs.(pcm_error) .> q / 2)
share = length(nz) / length(t_k)

println("量化器编解码器错误-均匀解码器(添加输出)")
println("  最大的模块: ", maximum(abs.(pcm_error)))
println("  SKZ的: ", round(rms(pcm_error), sigdigits = 5), ";q*√(非零值的分数): ", round(q * sqrt(share), sigdigits = 5))
println("  非零计数: ", length(nz), " 从 ", length(t_k), " (", round(100share, digits = 2), " %)")
println("  这些计数中的量化器值: ", unique(y_k[nz]))
println("  这些计数中的统一编码器代码: ", unique(c_k[nz]))
println("  量化器1.0级计数: ", count(==(1.0), y_k))
println("  每次计算的代码范围:从 ", minimum(c_k), " 以前 ", maximum(c_k))
Ошибка кодека Quantizer − Uniform Decoder (выход Add)
  наибольший модуль: 0.0078125
  СКЗ: 0.0015226; q·√(доля ненулевых): 0.0015226
  ненулевых отсчётов: 76 из 2001 (3.8 %)
  значения Quantizer в этих отсчётах: [1.0]
  коды Uniform Encoder в этих отсчётах: [127]
  отсчётов с уровнем Quantizer 1.0: 76
  диапазон кодов за расчёт: от 0 до 127

127码恢复为1-q。因此,信号顶部出现q值的误差。

考虑顶点附近的区域并标记误差边界。

In [ ]:
i0 = isempty(nz) ? argmax(y_k) : first(nz)
win = abs.(t_k .- t_k[i0]) .<= 0.3

plot(t_k[win], u_k[win];
    seriestype = :steppost, label = "Zero-Order Hold", linewidth = 2,
    xlabel = "时间,从", ylabel = "水平",
    title = "正弦波顶部的样本",
    legend = :outertopright, size = (1000, 450))
plot!(t_k[win], y_k[win]; seriestype = :steppost, label = "Quantizer", linewidth = 2)
plot!(t_k[win], d_k[win]; seriestype = :steppost, label = "Uniform Decoder", linewidth = 2, linestyle = :dash)
hline!([1.0, 1 - q]; label = "等级1.0和1-q", color = :gray, linestyle = :dot)

到1.0级,量化器和解码器的输出匹配。 在顶部,解码器被限制在127/128级别。

让我们比较量化和编码误差及其总结果。

In [ ]:
plot(t_k, quant_error;
    label = "quant_error: Quantizer − Zero-Order Hold", linewidth = 1,
    xlabel = "时间,从", ylabel = "错误",
    title = "模型点之间的误差",
    legend = :outertopright, size = (1100, 450))
plot!(t_k, pcm_error; label = "pcm_error: Quantizer − Uniform Decoder", linewidth = 1.5)
plot!(t_k, total_error; label = "total_error: Uniform Decoder − Zero-Order Hold", linewidth = 1, alpha = 0.6)
hline!([q / 2, -q / 2]; label = "±q/2", color = :gray, linestyle = :dash)

量化误差限制在±q/2,PCM误差出现在顶点处。 因此,总误差可能超过单个量化器的误差极限。

计算前两秒后的SQNR,不包括初始瞬态。

In [ ]:
steady = t_k .>= 2.0
p_signal = var(u_k[steady])

sqnr_quant = 10 * log10(p_signal / mean(abs2, quant_error[steady]))
sqnr_total = 10 * log10(p_signal / mean(abs2, total_error[steady]))

println("量化误差SQNR: ", round(sqnr_quant, digits = 1), " 分贝")
println("最终错误的SQNR:    ", round(sqnr_total, digits = 1), " 分贝")
println("理论评估:        ", round(sqnr_theory, digits = 1), " 分贝")
SQNR по ошибке квантования: 43.8 дБ
SQNR по итоговой ошибке:    42.9 дБ
Теоретическая оценка:        43.9 дБ

上电平限制增加了误差并降低了恢复信号的SQNR。

让我们找出信号均方根差最小的延迟。 让我们以1ms为增量检查0-0.5s范围。

In [ ]:
τ_grid = 0:0.001:0.5
lag_rms = [rms(d_k[steady] .- hold_at(x_sine, t_k[steady] .- τ)) for τ in τ_grid]
τ_est = τ_grid[argmin(lag_rms)]

println("数据延迟: ", τ_est, " c;单个滤波器的计算: ", round(τ_filter, digits = 4), " 与")
println("Skz的差异没有转变: ", round(rms(d_k[steady] .- hold_at(x_sine, t_k[steady])), sigdigits = 4))
println("Skz与转变的差异: ", round(minimum(lag_rms), sigdigits = 4))
Задержка по данным: 0.171 с; расчёт для одного фильтра: 0.1709 с
СКЗ разности без сдвига: 0.06158
СКЗ разности со сдвигом: 0.002467

让我们将找到的延迟与滤波器相位的计算进行比较,并在考虑到移位的情况下应用信号。

In [ ]:
plot(t_k, hold_at(x_sine, t_k);
    label = "Sine Wave", linewidth = 2,
    xlabel = "时间,从", ylabel = "的振幅",
    title = "发生器输出和PCM解码器输出",
    legend = :outertopright, size = (1100, 450))
plot!(t_k, d_k; label = "Uniform Decoder", linewidth = 2, linestyle = :dash)
plot!(t_k, hold_at(x_sine, t_k .- τ_est); label = "正弦波,τ的移位", linewidth = 1.5, linestyle = :dot)

延迟补偿后,信号几乎匹配。 移位之前的主要区别与滤波有关。

让我们看看模型使用了哪些代码。

In [ ]:
plot(t_k, c_k;
    seriestype = :steppost, label = "Uniform Encoder", linewidth = 1.5,
    xlabel = "时间,从", ylabel = "密码",
    title = "PCM编码器输出端的编码",
    legend = :outertopright, size = (1100, 400))
hline!([2^(B - 1) - 1]; label = "最大的代码是127", color = :gray, linestyle = :dash)

从0到127的代码用于此输入信号。 上限限制顶点恢复。

对于PWM,我们使用1ms网格和0.05s的周期。让我们计算每个周期的平均值;填充步骤为0.02。

In [ ]:
n_per = round(Int, T_pwm / Ts_pwm)
t_all = collect(0:Ts_pwm:last(x_pwm.time) - Ts_pwm)
n_periods = length(t_all) ÷ n_per
t_p = t_all[1:n_per * n_periods]     # 只有整个时期

pwm_p  = hold_at(x_pwm,  t_p)
duty_p = hold_at(x_duty, t_p)

pwm_avg  = vec(mean(reshape(pwm_p,  n_per, n_periods); dims = 1))
duty_avg = vec(mean(reshape(duty_p, n_per, n_periods); dims = 1))
t_avg    = T_pwm .* (0:n_periods - 1)
pwm_dev  = pwm_avg .- duty_avg

println("PWM输出值: ", first(sort(unique(pwm_p)), 5))
println("期间的计数: ", n_per, ";期间: ", n_periods)
println("每次计算的平均PWM输入: ", round(mean(duty_p), digits = 5))
println("每次计算的平均PWM输出: ", round(mean(pwm_p), digits = 5))
println("期间平均值之差:平均值 ", round(mean(pwm_dev), digits = 4),
        ", от ", round(minimum(pwm_dev), digits = 4), " до ", round(maximum(pwm_dev), digits = 4))
println("Средние выхода за период кратны 1/", n_per, ": ",
        all(isapprox.(pwm_avg .* n_per, round.(pwm_avg .* n_per); atol = 1e-9)))
Значения выхода PWM: [0.0, 1.0]
Отсчётов на период: 50; периодов: 400
Среднее входа PWM за расчёт: 0.49865
Среднее выхода PWM за расчёт: 0.488
Разность средних за период: среднее -0.0106, от -0.1275 до 0.0188
Средние выхода за период кратны 1/50: true

平均PWM值跟随输入。 所述偏差与在所述期间填充和改变输入的离散步骤有关。

让我们展示模拟前0.5秒的脉冲。

In [ ]:
win_p = t_p .<= 0.5

plot(t_p[win_p], pwm_p[win_p];
    seriestype = :steppost, label = "PWM输出", linewidth = 1.2,
    ylims = (-0.05, 1.05),
    xlabel = "时间,从", ylabel = "水平",
    title = "PWM输出和其输入端的控制信号",
    legend = :outertopright, size = (1100, 450))
plot!(t_p[win_p], duty_p[win_p]; label = "PWM输入", linewidth = 2)

随着输入信号的增大,脉冲宽度增大。

让我们比较平均PWM与输入并绘制它们的差异。

In [ ]:
p_avg = plot(t_avg, duty_avg;
    label = "PWM输入,周期内的平均值", linewidth = 2,
    ylabel = "水平", title = "PWM周期的平均值",
    legend = :outertopright)
plot!(p_avg, t_avg, pwm_avg; seriestype = :steppost, label = "PWM输出,周期内的平均值", linewidth = 1.5)

p_dev = plot(t_avg, pwm_dev;
    seriestype = :steppost, label = "出口-入口", linewidth = 1.5,
    xlabel = "时间,从", ylabel = "差异",
    legend = :outertopright)
hline!(p_dev, [1 / n_per, -1 / n_per]; label = "±ΔD", color = :gray, linestyle = :dash)

plot(p_avg, p_dev; layout = (2, 1), size = (1100, 700))

上图显示了对输入平均值的跟踪,下图显示了偏差。

我们将输出测量的误差,SQNR和延迟。

In [ ]:
println("量化步长q:                    ", q)
println("max |quant_error| / q:                ", round(maximum(abs.(quant_error)) / q, digits = 3))
println("СКЗ quant_error / (q/√12):            ", round(rms(quant_error) / (q / sqrt(12)), digits = 3))
println("max |pcm_error| / q:                  ", round(maximum(abs.(pcm_error)) / q, digits = 3))
println("具有编码器饱和度的样本百分比, %: ", round(100share, digits = 2))
println("Задержка фильтра: расчёт / данные, с: ", round(τ_filter, digits = 3), " / ", τ_est)
println("平均(输出−输入)PWM:           ", round(mean(pwm_dev), digits = 4))
@assert maximum(abs.(quant_error)) <= q/2 + 1e-8
@assert all(isfinite, d_k) && all(isfinite, pwm_dev)
string("PCM PWM passed; samples=", length(t_k), "; SQNR=", round(sqnr_quant; digits=1), " dB")
Шаг квантования q:                    0.0078125
max |quant_error| / q:                0.5
СКЗ quant_error / (q/√12):            0.986
max |pcm_error| / q:                  1.0
Доля отсчётов с насыщением кодера, %: 3.8
Задержка фильтра: расчёт / данные, с: 0.171 / 0.171
Среднее (выход − вход) PWM:           -0.0106

结论

该模型显示了量化和PCM范围限制对信号重建精度的影响。 量化误差不超过半步,滤波器延迟约为0.17s,平均PWM值跟随输入信号。