Baseband modulation
This example shows a simple way to perform baseband modulation by multiplying a modulated complex signal by a complex sine wave to perform frequency conversion with boost. It is preferable to model the system in a complex baseband. However, there are some circumstances in which it is necessary to simulate the system in real bandwidth. An example of this is when a signal from an adjacent frequency band is processed with non-linearity and causes interference in the desired frequency band. This example also illustrates the effect of such an intervention and is presented in two versions.:
- In the form of a script;
- In the form of a model.
The algorithm scheme looks like this:
Building a system using a script
The first step of building a system is to connect all the system elements and libraries used.
Pkg.add(["CSV"])
using FFTW, Plots
import .EngeeComms
gr()
step = EngeeComms.step;
Let's create a structure that contains all the variables associated with the parameters of the connected blocks.
mutable struct passband_modulator
# Transmitter
obj_random_integer
obj_QPSK_modulator
obj_raised_cosine_transmit
# Upconverter
obj_sine_upc
obj_prod_upc
obj_re_upc
# Channel
obj_sum
obj_AWGN
# interference
obj_sine_inter
obj_re_inter
obj_const_inter
obj_math_inter
obj_gain_inter
# Receiver
# downconverter
obj_sine_wave_dc
obj_conj_dc
obj_prod_dc
obj_raised_cosine_receive
obj_QPSK_demodulator
# Metrics
obj_int_to_bit
obj_BER
obj_delay
obj_EVM
function passband_modulator()
new(EngeeComms.random_integer(4, 37, 1e-6, 100, "Int"), EngeeComms.QPSK(pi/4), EngeeComms.rcostrans(0.2, 8, 8, 1.0),
EngeeComms.sine_wave(1.0, 2500000.0, 0.0, "Discrete", "Complex", "Trigonometric fcn", 1e-6/8, 800, "Restart at time zero"), EngeeComms.product(2, "Element-wise"), EngeeComms.complex_to_real_imag("Real"),
EngeeComms.Sum("+"), EngeeComms.AWGN(67, "mode", 8.0, 2, 1/(2*8), 1e-6, 1e-4), EngeeComms.sine_wave(1.0 ,8.833333333333334e+05, 0.0, "Discrete", "Complex", "Trigonometric fcn", 1e-6/8, 800, "Restart at time zero"), EngeeComms.complex_to_real_imag("Real"), 3, EngeeComms.math("pow"), EngeeComms.Gain(.25),
EngeeComms.sine_wave(1.0 ,2500000.0, 0.0, "Discrete", "Complex", "Trigonometric fcn", 1e-6/8, 800, "Restart at time zero"), EngeeComms.math("conj"), EngeeComms.product(2, "Element-wise"), EngeeComms.rcosreciev(0.2, 8, 8, 2.0, 8, 0), EngeeComms.deQPSK(pi/4),
EngeeComms.IntToBit(2, "Unsigned", "MSB first", "Boolean"), EngeeComms.error_rate(16, 0, "Entire frame", "Port", false, false), EngeeComms.delay(8,1), EngeeComms.EVM("Average reference signal power", "Input port", "Input length", 1))
end
end
Let's create a function to perform the logic of the system.
function step1(obj::passband_modulator)
out_upconv1 =[];
out_downcov =[];
out_rec_filt =[];
out_interfer =[];
out_BER =zeros(3);
EngeeComms.setup(obj.obj_random_integer);
EngeeComms.setup(obj.obj_AWGN, [1]);
EVM_out = 0;
for i =1:201
# The transmitter
ri = step(obj.obj_random_integer);
QPSK_tr = step(obj.obj_QPSK_modulator, ri);
raised_cos_tr = step(obj.obj_raised_cosine_transmit, QPSK_tr)
test_rcos = raised_cos_tr;
# Boost converter
sine_wave_upc = step(obj.obj_sine_upc);
prod_upc = step(obj.obj_prod_upc, sine_wave_upc,raised_cos_tr);
out_upconv = step(obj.obj_re_upc, prod_upc);
# Channel
# Interference with nonlinear distortion
sine_wave_nonl = step(obj.obj_sine_inter);
re_nonl = step(obj.obj_re_inter, sine_wave_nonl);
inter_pow = step(obj.obj_math_inter, re_nonl, obj.obj_const_inter);
inter_gain = step(obj.obj_gain_inter, inter_pow);
AWGN_in = step(obj.obj_sum, out_upconv, inter_gain);
Channel_out = step(obj.obj_AWGN, AWGN_in);
# The receiver
# Step-down converter
sine_dc = step(obj.obj_sine_wave_dc);
conj_dc = step(obj.obj_conj_dc, sine_dc);
out_dc = step(obj.obj_prod_dc, conj_dc, Channel_out);
raised_cos_reciev = step(obj.obj_raised_cosine_receive, out_dc[:,1]);
out_reciever = step(obj.obj_QPSK_demodulator, raised_cos_reciev);
# Calculation of the bit error (BER)
out_bits = step(obj.obj_int_to_bit, out_reciever);
rand_int_bits = step(obj.obj_int_to_bit, ri);
BER = step(obj.obj_BER, rand_int_bits, out_bits);
# Calculation of EVM_rms
ref_sig = step(obj.obj_delay, QPSK_tr)
EVM_out = step(obj.obj_EVM, ref_sig, raised_cos_reciev)
# Outputs
out_BER = BER;
out_upconv1 = vcat(out_upconv1, out_upconv);
out_downcov = vcat(out_downcov, out_dc);
out_rec_filt = vcat(out_rec_filt, raised_cos_reciev);
out_interfer = vcat(out_interfer, inter_gain);
end
return out_BER, out_upconv1, out_downcov, out_rec_filt, out_interfer, EVM_out;
end
Starting the system and receiving the output data.
passband = passband_modulator() # Calling the structure
BER,Upconverter,Downconverter,receive_filter,Interference, EVM_out = step1(passband); # Function call
println(BER[1]," BER;") # Output of data on the command line
println(BER[2], " Bit errors;")
println(BER[3], " Bits;")
println()
println(EVM_out, " EVM_rms.") # Plotting a graph
Let's plot the signal spectrum at the output of the transmitter and the spectrum of introduced distortions.
b = Float64.(Upconverter);
spec_b = fftshift(fft(b))[80400:160800];
plot([0:4e6/80400:4e6...],(20log10.(abs.((spec_b)./[4e6/80400,4e6/80400:4e6/80400:4e6...]))), label ="The signal at the output of the transmitter")
e = Float64.(Interference);
spec_e = fftshift(fft(e))[80400:160800];
plot!([0:4e6/80400:4e6...],20log10.(abs.((spec_e))), label ="Distortions")
ylabel!("Power DBW")
xlabel!("Frequency Hz")
plot!(legend=:outerbottom)
ylims!(-240, -60)
xlims!(0.2e6,4e6)
The spectrum of the signal after passing the step-down converter.
d = ComplexF64.(Downconverter);
spec_d = fftshift(fft(d));
plot([-4e6:8e6/160800:4e6-1...],10log10.(abs.((spec_d/3e6))), label ="The output signal of the step-down converter")
ylabel!("Power DBW")
xlabel!("Frequency MHz")
plot!(legend=:outerbottom)
The scheme of the signal constellation.
f = ComplexF64.(receive_filter);
plot(f[end-200:end], seriestype=:scatter)
plot!([0.75+0.75im, 0.75-0.75im, -0.75+0.75im, -0.75-0.75im], seriestype=:scatter, lc=:red, lw=:5, ls =:dot)
plot!(legend=:none)
xlabel!("Single-phase amplitude")
ylabel!("Quadrature amplitude")
Building a model
Now let's consider the second way to build communication systems, which is to model digital signal processing using Engee models.
Passbanmod
Interference with Nonlinearity
Upconverter
Downconverter
Calculate RMS EVM
Compute BER
function run_model( name_model, path_to_folder )
Path = path_to_folder * "/" * name_model * ".engee"
if name_model in [m.name for m in engee.get_all_models()] # Checking the condition for loading a model into the kernel
model = engee.open( name_model ) # Open the model
model_output = engee.run( model, verbose=true ); # Launch the model
else
model = engee.load( Path, force=true ) # Upload a model
model_output = engee.run( model, verbose=true ); # Launch the model
engee.close( name_model, force=true ); # Close the model
end
return model_output
end
# Launching the model
run_model( "passbandmod", @__DIR__ )
# Connecting libraries
using CSV, DataFrames
using Plots # Connecting the library for charting
gr() # Enabling a backend graphics display method
# # EVM
EVM = Matrix(CSV.read("$(@__DIR__)/EVM.csv", DataFrame)); # uploading data
plot(EVM[:,1], EVM[:,2], xlabel="Time", ylabel="EVM", title="EVM") # Plotting a graph
# # BER
BER = Matrix(CSV.read("$(@__DIR__)/BER.csv", DataFrame)); # uploading data
plot(BER[:,1], BER[:,2], xlabel="Time", ylabel="BER", title="BER") # Plotting a graph
Conclusion
We have implemented a script and a model of a simple way to perform baseband modulation by multiplying a modulated complex signal by a complex sine wave to perform frequency conversion with an increase. They also showed how to use scripts and models for digital signal processing in Engee.




