Влияние разрядности АЦП на приём 16 QAM
Влияние разрядности АЦП на приём 16 QAM
В этом примере исследуем квантование сигнала 16-QAM в модели Engee. Два блока Flash ADC оцифровывают синфазную и квадратурную составляющие. Сравним разрешение 2 и 4 бита по созвездию и модулю вектора ошибки.
16-QAM — квадратурная амплитудная модуляция с 16 состояниями, каждое из которых передаёт четыре бита. Состояние задаётся комплексным отсчётом . Аналого-цифровой преобразователь (АЦП) заменяет непрерывный уровень одним из конечного числа кодов. При малой разрядности расстояние между уровнями велико, и точки созвездия смещаются.
Для идеального равномерного квантования шаг при битах и полном диапазоне равен . Ошибку комплексных символов оценим по EVM (error vector magnitude):
Здесь — исходные символы, — восстановленные из цифровых кодов. EVM включает и шум канала, и ошибку квантования; это не отдельное измерение шума АЦП.

Модель FlashADC_QAM.engee содержит Random Integer Generator → Rectangular QAM Modulator Baseband → ReceiverNoise → Complex to Real-Imag → I_ADC/Q_ADC. Генератор создаёт значения от 0 до 15.
АЦП используют внутренний такт 500 Гц и знаковое представление кодов. Симуляция работает с переменным шагом, необходимым внутренней схеме блока. Записываются опорное созвездие и цифровые коды I и Q.
Подключим библиотеки и зададим путь к модели относительно папки скрипта. Вспомогательная функция загрузит и откроет модель, применит параметры и закроет её после запуска. Данные To Workspace читаются из общей рабочей области Engee, поэтому программный запуск этого примера выполняем с isolate = false.
using Statistics, DataFrames, Plots
gr()
name = "FlashADC_QAM"
path = joinpath(@__DIR__, name * ".engee")
@assert isfile(path) "Не найден файл модели: $path"
function load_and_run(path, name; settings=Pair{String,String}[])
try engee.close(name; force=true) catch end
model = engee.load(path; force=true)
engee.open(name)
for (block, keyvalue) in settings
engee.set_param!("$name/$block", keyvalue)
end
try
engee.run(model)
finally
engee.close(name; force=true)
end
end
wa(name) = Core.eval(Main, :(collect($(Symbol(name)))))
Выполним модель для двух значений разрядности. Восстановим квадратуры из знаковых кодов делением на и рассчитаем EVM. Первые три отсчёта исключим как переходный процесс.
function adc_run(bits)
load_and_run(path,name;settings=["I_ADC"=>("NBits"=>string(bits)),
"Q_ADC"=>("NBits"=>string(bits))])
ref = wa("reference"); i = wa("i_codes"); q = wa("q_codes")
n = minimum((nrow(ref),nrow(i),nrow(q)))
valid = 4:n
ideal = ComplexF64.(ref.value[valid])
decoded = complex.(Float64.(i.value[valid]),Float64.(q.value[valid])) ./ 2.0^(bits-1)
evm = sqrt(mean(abs2.(decoded .- ideal)) / mean(abs2.(ideal)))
ideal,decoded,evm
end
ideal2,decoded2,evm2 = adc_run(2)
ideal4,decoded4,evm4 = adc_run(4);
Построим исходное и оцифрованное созвездия, а рядом покажем EVM при 2 и 4 битах.
p1 = scatter(real.(ideal4),imag.(ideal4),label="Идеальные",xlabel="I",ylabel="Q",title="Созвездие 16-QAM",aspect_ratio=:equal)
scatter!(p1,real.(decoded2),imag.(decoded2),label="АЦП 2 бита",alpha=0.55)
scatter!(p1,real.(decoded4),imag.(decoded4),label="АЦП 4 бита",alpha=0.55)
p2 = bar(["2 бита","4 бита"],100 .* [evm2,evm4],ylabel="EVM, %",title="Ошибка модуляции",legend=false)
display(plot(p1,p2,layout=(1,2),size=(1000,450)))
println("EVM 2 бита=",100*evm2,"%; EVM 4 бита=",100*evm4,"%")
println(string("EVM_2=",evm2,"; EVM_4=",evm4))
При двух битах уровни квантования грубее, поэтому точки сильнее отклоняются от опоры. При четырёх битах ошибка квантования уменьшается, но шум канала остаётся.
Наблюдаемая EVM зависит также от масштаба входа, диапазона АЦП и момента выборки. При перегрузке увеличение разрядности само по себе не устранит ограничение амплитуды.
Вывод
В данном примере мы показали, как в Engee оценить влияние разрядности АЦП на модулированный сигнал. Совместное использование созвездия и EVM помогает выбрать разрешение преобразователя. Для полного проектирования приёмника следует дополнительно исследовать перегрузку, масштабирование входа и синхронизацию.
