Engee documentation
Notebook

Digital communication channel

In this example, we demonstrate the block chain for creating a digital communication channel. The model includes a modulator, receive and transmit filters for Manchester coding, a white noise channel, signal decoding and error counting.

The example takes advantage of Engee's ability to work with models in which, for individual subsystems, different simulation rates are set. In this model, there are subsystems operating at different frequencies:

  • 2.5 Hz - frequency at the "application level" of the model (the generator generates numbers from 0 to 10)
  • 10 Hz - frequency of bit stream after digital modulation (4-bit packets)
  • 80 Hz - frequency at the physical level (bipolar digital signal and Manchester coding 1 to 8)

On the receiving side, error counting is performed at both number and bit levels.

Model structure

image.png

The model includes:

  • Generator of numbers from 0 to 10
  • Modulator which converts them into 4-bit sequences Converter to convert a unipolar signal to bipolar. Digital filter to apply the Manchester code to the signal.
  • Channel with added Gaussian noise

And a set of inverse operations:

  • Coupled **receiving filter for the Manchester code
  • Converter from bipolar to unipolar signal
  • Bit Error Counting Unit between the sent and received signal.
  • Demodulator which makes numbers from 0 to 10 from each group of 4 bits

General view of the model

image.png

Such a model allows you to look inside each communication line and see what the signal is like at each stage of transmission.

Running the model from a script

To automate the analysis, you need to close the model (if it is open in the canvas) and run that model under software control.

In [ ]:
Pkg.add(["Measures"])
In [ ]:
# Запуск модели
if "simple_digital_channel" in [m.name for m in engee.get_all_models()]
    model = engee.open( "simple_digital_channel" );
else
    model = engee.load( "$(@__DIR__)/simple_digital_channel.engee" );
end

results = engee.run( model )
Out[0]:
Dict{String, DataFrames.DataFrame} with 9 entries:
  "Кол-во ошибок передачи"    => 101×2 DataFrame…
  "Приемный фильтр"           => 801×2 DataFrame…
  "Выходной бинарный вектор"  => 26×2 DataFrame…
  "Входной сигнал и задержка" => 26×2 DataFrame…
  "Бинарный вектор"           => 26×2 DataFrame…
  "Входной сигнал"            => 26×2 DataFrame…
  "Сигнал в канале"           => 801×2 DataFrame…
  "Формирующий фильтр"        => 801×2 DataFrame…
  "Реконструкция сигнала"     => 26×2 DataFrame
In [ ]:
#engee.close( "simple_digital_channel", force=true );

Graph output

Connecting libraries

In [ ]:
# Подключение библиотек
using DataFrames, Plots, Measures
gr(); # Подключение бэкенда - метода отображения графики

Analysing input and output information (number series)

In [ ]:
# Загрузка данных
Sin = results["Входной сигнал и задержка"];
Sout = results["Реконструкция сигнала"];

# Построение графиков
plot(
    plot( Sin.time, Sin.value, st=:step, xlabel="Время", ylabel="Числа", title="Числа на входе", leg=false ),
    plot( Sout.time, Sout.value, st=:step, xlabel="Время", ylabel="Числа", title="Числа на выходе", leg=false ),
    layout=grid(1, 2, widths=(4/8,4/8)), size=(900,300), margin=5mm, guidefont = font( 7 )
)
Out[0]:

Analysing bit arrays at input and output

In [ ]:
# Загрузка данных
Bin = results["Бинарный вектор"]
Bout = results["Выходной бинарный вектор"]

Bin_a = [v[1] for v in Bin.value]
Bin_b = [v[2] for v in Bin.value]
Bin_c = [v[3] for v in Bin.value]
Bin_d = [v[4] for v in Bin.value]

Bout_a = [v[1] for v in Bout.value]
Bout_b = [v[2] for v in Bout.value]
Bout_c = [v[3] for v in Bout.value]
Bout_d = [v[4] for v in Bout.value]

# Построение графиков
plot( 
    plot( Bin.time, [Bin_a Bin_b.+1.1 Bin_c.+2.2 Bin_d.+3.3], st=:step,
      xlabel="Время", ylabel="Биты", title="Входной массив", leg=false ),
    plot( Bout.time, [Bout_a Bout_b.+1.1 Bout_c.+2.2 Bout_d.+3.3], st=:step,
      xlabel="Время", ylabel="Биты", title="Выходной массив", leg=false ),
    layout=grid(1, 2, widths=(4/8,4/8)), size=(900,300), margin=5mm, guidefont = font( 7 )
)
Out[0]:

Let's display the number of erroneously received bits

In [ ]:
# Загрузка данных
ERC = results["Кол-во ошибок передачи"]

# Построение графиков
plot( 
    plot( ERC.time, ERC.value, st=:step, xlabel="Время", ylabel="Сигнал", title="Количество ошибок приема", leg=false ),
    size=(900,400), margin=5mm, guidefont = font( 7 )
)
Out[0]:

Let's study the noise in the channel

In [ ]:
# Загрузка данных
Fin = results["Формирующий фильтр"];
AWGN_out = results["Сигнал в канале"];
Fout = results["Приемный фильтр"];

# Построение графиков
plot( 
    plot( Fin.time, Fin.value, st=:step, xlabel="Время", ylabel="Сигнал", title="Сигнал из формирующиего фильтра (идеальный)", leg=false ),
    plot( AWGN_out.time, AWGN_out.value, st=:step, xlabel="Время", ylabel="Сигнал", title="Сигнал в канале (с белым шумом)", leg=false ),
    plot( Fout.time, Fout.value, st=:step, xlabel="Время", ylabel="Сигнал", title="Сигнал после принимающего фильтра", leg=false ),
    layout=grid(3, 1),
    size=(900,600), margin=5mm, guidefont = font( 7 )
)
Out[0]:

Conclusion

Based on this example, you can calculate coding redundancy parameters, check error correction codes or check the operation of a communication channel at the physical level with a different signal coding: frequency, phase, make the channel a part of a virtual test bench and much more.

The Engee platform allows you to demonstrate the operation of digital electronics, select parameters and debug algorithms in a very visual way and support everything with visual illustrations.