使用PCM和PWM进行信号转换
在这个例子中,让我们比较AnyMath模型中的脉冲编码调制(PCM)和脉冲宽度调制(PWM)。 让我们测量量化和编码误差,估计滤波器延迟,并检查平均PWM值对输入信号的依赖性。
采样和量化
采样时,我们取样 . 要恢复带限信号,采样频率必须超过频谱上限频率的两倍。
量化器以增量对样本进行舍入 :
对于均匀分布的误差,其均方根值估计为 .
编码和解码
为 位和范围 步等于 . 签名代码从 以前 ,并且解码器将代码乘以 . 可恢复水平上限 — .
信号功率与量化误差功率之比:
脉冲宽度调制
PWM信号在0和1之间切换。 其在周期内的平均值等于填充因子:
用离散的步骤 填充增量变化 .
模型描述
模型 pcm_pwm_analysis 如下图所示。
.png)
该模型包含PCM和PWM分支。 在PCM中依次执行滤波、采样、量化、编码和解码。
| 错误信号 | 正在比较的是什么 |
|---|---|
| quant_error | 量化器的输入和输出 |
| pcm_error | 量化和解码信号 |
| 总计-错误 | 量化器输入和解码信号 |
我们分别评估滤波器的延迟与编码错误。
让我们准备分析函数。
using Main: engee, plot, plot!, histogram, hline!, savefig, Animation, frame, gif
using Statistics
让我们设置模型参数并计算量化步长和预期SQNR。
# 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)
最大签名代码为127,因此我们无法准确表示1.0级别。 对于所使用的信号幅度,预期的SQNR约为44dB。
巴特沃斯滤波器的传递函数是从其极点获得的:
让我们通过模数和相位来估计信号频率处的增益和延迟 .
# 模拟巴特沃斯滤波器的极点和频率响应
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), " 与")
一个滤波器在接近单位的增益下提供约0.17s的延迟。 两个连续的滤波器大致加倍延迟。
从示例文件夹下载模型并运行计算。
model_name = "pcm_pwm_analysis"
model_file = joinpath(@__DIR__, model_name * ".engee")
model = engee.load(model_file; force = true)
results = engee.run(model);
我们将接收记录的信号并关闭模型。
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
信号记录在不同的时间网格上。 让我们把它们带到一个共同的网格进行比较。
Hold_at函数选择指定时间点之前的最后一个倒计时。 我们用它来比较脚本错误和模型块的输出。
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)))
计算出的PCM误差与相应添加块的信号重合。
量化误差
让我们检查量化误差及其RMS值的限制。
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))
误差不超过±q/2。 让我们将其分布与q/√12的估计值进行比较。
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发生了什么。
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))
127码恢复为1-q。因此,信号顶部出现q值的误差。
考虑顶点附近的区域并标记误差边界。
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级别。
让我们比较量化和编码误差及其总结果。
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,不包括初始瞬态。
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。
让我们找出信号均方根差最小的延迟。 让我们以1ms为增量检查0-0.5s范围。
τ_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))
让我们将找到的延迟与滤波器相位的计算进行比较,并在考虑到移位的情况下应用信号。
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)
延迟补偿后,信号几乎匹配。 移位之前的主要区别与滤波有关。
让我们看看模型使用了哪些代码。
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。
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.5秒的脉冲。
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与输入并绘制它们的差异。
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和延迟。
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")
结论
该模型显示了量化和PCM范围限制对信号重建精度的影响。 量化误差不超过半步,滤波器延迟约为0.17s,平均PWM值跟随输入信号。
.png)
.png)
.png)
.png)
.png)
.png)
.png)