Engee documentation
Notebook

4-FSK modulator and demodulator

In this example we will look at working with 4-FSK (Frequency Shift Keying) in Engee. Frequency modulation is a type of modulation in which information is encoded by changing the frequency of a signal. 4-FSK, Four Level Shift Keying, is the type of modulation used in DMR (Digital Mobile Radio) and is also optimal for use in PMR (Professional Mobile Radio) systems.

Each pair of bits of information defines a frequency shift relative to the carrier frequency.

The envelope in this type of modulation is constant, which offers significant advantages in consumption and transmitter circuit design: There are no stringent linearity requirements for the transmission path.

image.png

We will build a 4-FSK modulator and demodulator on basic elements.

In [ ]:
function run_model( name_model)
    Path = (@__DIR__) * "/" * name_model * ".engee"
    if name_model in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
        model = engee.open( name_model ) # Открыть модель
        model_output = engee.run( model, verbose=true ); # Запустить модель
    else
        model = engee.load( Path, force=true ) # Загрузить модель
        model_output = engee.run( model, verbose=true ); # Запустить модель
        engee.close( name_model, force=true ); # Закрыть модель
    end
    sleep(5)
    return model_output
end
Out[0]:
run_model (generic function with 1 method)

The model contains two basic blocks: modulator and demodulator. The input is a bit stream, after which we form packets of two samples using a buffer and perform modulation and demodulation. In our example, the conditions for modulator and demodulator operation are ideal: there is no communication channel and no interference.

image.png

Next, we run the model and analyse the simulation results.

In [ ]:
run_model("4fsk")
Building...
Progress 100%
Out[0]:
Dict{String, DataFrame} with 4 entries:
  "4-FSK demodulator.Out1" => 51×2 DataFrame…
  "Buffer.1"               => 51×2 DataFrame…
  "SumError.ErrorCnt"      => 101×2 DataFrame…
  "Add.1"                  => 101×2 DataFrame

Let's compare the input group of bits with the output bits.

In [ ]:
In_bit = collect(simout["4fsk/4-FSK demodulator.Out1"]);
Out_bit = collect(simout["4fsk/Buffer.1"]);
In_and_Out_bit = [In_bit.value Out_bit.value]
Out[0]:
51×2 Matrix{Vector{Float64}}:
 [0.0, 0.0]  [0.0, 0.0]
 [1.0, 1.0]  [1.0, 1.0]
 [0.0, 1.0]  [0.0, 1.0]
 [1.0, 0.0]  [1.0, 0.0]
 [0.0, 1.0]  [0.0, 1.0]
 [1.0, 1.0]  [1.0, 1.0]
 [0.0, 1.0]  [0.0, 1.0]
 [1.0, 0.0]  [1.0, 0.0]
 [1.0, 0.0]  [1.0, 0.0]
 [0.0, 1.0]  [0.0, 1.0]
 [1.0, 1.0]  [1.0, 1.0]
 [1.0, 0.0]  [1.0, 0.0]
 [1.0, 1.0]  [1.0, 1.0]
 ⋮           
 [0.0, 0.0]  [0.0, 0.0]
 [1.0, 0.0]  [1.0, 0.0]
 [0.0, 1.0]  [0.0, 1.0]
 [1.0, 1.0]  [1.0, 1.0]
 [1.0, 0.0]  [1.0, 0.0]
 [1.0, 1.0]  [1.0, 1.0]
 [0.0, 1.0]  [0.0, 1.0]
 [0.0, 0.0]  [0.0, 0.0]
 [1.0, 1.0]  [1.0, 1.0]
 [1.0, 0.0]  [1.0, 0.0]
 [1.0, 0.0]  [1.0, 0.0]
 [0.0, 0.0]  [0.0, 0.0]

As we can see from the explicit comparison of bit pairs, all sequences are recognised correctly. Now let's look at the numerical error rate in our model.

In [ ]:
ErrorCnt = collect(simout["4fsk/SumError.ErrorCnt"]);
ErrorCnt = ErrorCnt.value;
print("Кол-во ошибок за всё время моделирования: $(ErrorCnt[end])")
Кол-во ошибок за всё время моделирования: 0.0

Conclusion

The simulation results are as follows: our released modulator and demodulator work correctly, without errors. This means that such an implementation can be applied to communication protocols such as DMR.