Engee documentation
Notebook

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.:

  1. In the form of a script;
  2. In the form of a model.

The algorithm scheme looks like this:

passband.png

Building a system using a script

The first step of building a system is to connect all the system elements and libraries used.

In [ ]:
Pkg.add(["CSV"])
In [ ]:
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.

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

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

Starting the system and receiving the output data.

In [ ]:
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
0.001493131594664543 BER;
60.0 Bit errors;
40184.0 Bits;

35.140576755325306 EVM_rms.

Let's plot the signal spectrum at the output of the transmitter and the spectrum of introduced distortions.

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

The spectrum of the signal after passing the step-down converter.

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

The scheme of the signal constellation.

In [ ]:
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")
Out[0]:

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

image3.png

Interference with Nonlinearity

image.png

Upconverter

image.png

Downconverter

image.png

Calculate RMS EVM

image.png

Compute BER

image.png
In [ ]:
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__ )
Building...
Progress 0%
Progress 5%
Progress 10%
Progress 15%
Progress 21%
Progress 26%
Progress 31%
Progress 37%
Progress 42%
Progress 47%
Progress 53%
Progress 59%
Progress 64%
Progress 69%
Progress 74%
Progress 79%
Progress 85%
Progress 91%
Progress 96%
Progress 100%
Progress 100%
In [ ]:
# Connecting libraries
using CSV, DataFrames
using Plots # Connecting the library for charting
gr() # Enabling a backend graphics display method
Out[0]:
Plots.GRBackend()
In [ ]:
# # 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
Out[0]:
In [ ]:
# # 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
Out[0]:

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.