Engee documentation
Notebook

The effect of ADC bit depth on 16 QAM reception

In this example, we study the quantization of the 16-QAM signal in the Engee model. Two Flash ADC units digitize the common-mode and quadrature components. Let's compare the resolution of 2 and 4 bits in terms of the constellation and the modulus of the error vector.

16-QAM is a quadrature amplitude modulation with 16 states, each of which transmits four bits. The state is set by a complex count . An analog-to-digital converter (ADC) replaces the continuous level with one of a finite number of codes. With low bit depth, the distance between the levels is large, and the constellation points shift.

For an ideal uniform quantization, the step at bits and the full range equal to . The error of complex symbols is estimated by EVM (error vector magnitude):

Here — the original characters, — recovered from digital codes. The EVM includes both channel noise and quantization error; it is not a separate measurement of ADC noise.

image.png

Model FlashADC_QAM.engee contains Random Integer Generator → Rectangular QAM Modulator Baseband → ReceiverNoise → Complex to Real-Imag → I_ADC/Q_ADC. The generator creates values from 0 to 15.

ADCs use an internal clock cycle of 500 Hz and a signed representation of codes. The simulation works with the variable pitch required by the internal block circuit. The reference constellation and numeric codes I and Q are recorded.

Connect the libraries and set the path to the model relative to the script folder. The auxiliary function will load and open the model, apply the parameters, and close it after startup. The data To Workspace is read from the Engee shared workspace, so we run this example programmatically with isolate = false.

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

Let's run the model for two bit depth values. Let's restore the quadratures from the sign codes by dividing by and calculate the EVM. We will exclude the first three counts as a transitional process.

In [ ]:
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);

Let's build the original and digitized constellations, and next to them we'll show the EVM at 2 and 4 bits.

In [ ]:
p1 = scatter(real.(ideal4),imag.(ideal4),label="Perfect",xlabel="I",ylabel="Q",title="Constellation 16-QAM",aspect_ratio=:equal)
scatter!(p1,real.(decoded2),imag.(decoded2),label="ADC 2 bits",alpha=0.55)
scatter!(p1,real.(decoded4),imag.(decoded4),label="ADC 4 bits",alpha=0.55)
p2 = bar(["2 бита","4 бита"],100 .* [evm2,evm4],ylabel="EVM, %",title="Modulation error",legend=false)
display(plot(p1,p2,layout=(1,2),size=(1000,450)))
println("EVM 2 bits=",100*evm2,"%; EVM 4 bits=",100*evm4,"%")
println(string("EVM_2=",evm2,"; EVM_4=",evm4))
No description has been provided for this image
EVM 2 бита=133.06140497035122%; EVM 4 бита=138.0023397577081%
EVM_2=1.3306140497035122; EVM_4=1.380023397577081

With two bits, the quantization levels are rougher, so the points deviate more strongly from the support. At four bits, the quantization error decreases, but the channel noise remains.

The observed EVM also depends on the input scale, ADC range, and sampling moment. In case of overload, increasing the bit depth alone will not eliminate the amplitude limitation.

Conclusion

In this example, we have shown how to evaluate the effect of the ADC bit rate on the modulated signal in Engee. The combined use of constellation and EVM helps to select the resolution of the converter. For a complete receiver design, overload, input scaling, and synchronization should be further investigated.