Engee documentation
Notebook

Investigating error detection using CRC

This example examines how Cyclic Redundancy Check (CRC) detects errors when transmitting packets over a noisy binary channel. The Engee model transmits random packets, adds CRC verification bits to them, distorts them in a binary symmetric channel, checks the syndrome on reception, and counts correct, detected, and undetected erroneous packets. The model is assembled from Engee library blocks, including the General CRC Generator and General CRC Syndrome Detector. The script manages the model programmatically and compares the simulation results with an accurate theory calculated from the weight spectrum of the code. Experiments can also be set up in a web application based on the Genie Framework, which runs the same model. The example is useful for engineers and students studying noise-tolerant coding and statistical modeling of communication channels.

In any communication channel, some of the bits are distorted by interference. In order for the receiver to know about the error, the transmitter adds redundant verification bits to the data. CRC is one of the most common methods of such verification: it is used in Ethernet, USB, industrial network protocols, in file archives and in data storage.

CRC only detects errors, but does not correct them. The receiver discards the error packet or requests it again. Therefore, the main indicator of CRC quality is the probability that an error will go unnoticed.

In this example, we:

  1. Let's briefly analyze the CRC theory and deduce the exact probabilities of three packet reception outcomes.
  2. Let's study the model and test it on experiments with a pre-known answer.
  3. Let's compare the simulation results with the theory in a wide range of error probability.
  4. Let's launch a web application in which experiments can be performed without programming.

CRC Theory

The bits of the packet are considered as coefficients of a polynomial over the field GF(2), where addition is the exclusive OR. Information Bits form a polynomial degrees less . The generating polynomial degrees set by a list of degrees: [5, 3, 0] means .

The CRC encoder assigns the remainder of the division to the data:

Full package length , at the initial settings , , . Any code word divided into without a trace.

The receiver calculates the syndrome, which is the remainder of dividing the received word by . If the channel has added an error vector , the syndrome is equal to . The error is not detected only if it is itself a non-zero codeword.

A binary symmetric channel independently inverts each bit with probability . Each package falls into exactly one of the three classes:

where — the number of weight code words (the weight range of the code). By all the error vectors are equally likely, and the proportion of undetected errors is : from for vectors with zero syndrome, it is necessary to exclude the only case without errors. For and It's almost .

Description of the model

crc_error_detection_model.png

Model crc_error_detection.engee It is compiled only from Engee library blocks and works in a fixed step. . One step is one packet, so simulation time means the packet number, not the physical time.

Knot Engee Blocks Appointment
Data source Bernoulli Binary Generator Payload random information bits at each step
The coder General CRC Generator CRC Encoder adds the verification bits
Channel Bernoulli Binary Generator Channel Noise, Logical Operator XOR Binary Symmetric Channel error vector with probability of one adds up to the XOR code word
The detector Data Type Conversion To Double, General CRC Syndrome Detector CRC Detector highlights information bits and a sign of a non-zero syndrome
Error handling Logical Operator (XOR, OR, NOT, AND, NOR), Sum of Elements, Mux, Data Type Conversion generates the event vector of the package
Counters Add Packet Statistics and Unit Delay Previous Totals accumulating events
Stop Constant, Relational Operator, Logical Operator OR, Stop Simulation the model is stopped by the limits

The error handling node generates a packet event vector at each step.:

where — a sign of a non-zero syndrome, — a sign that at least one information bit has changed. The vector elements mean: a packet has been transmitted, an error has been detected, an error has been missed, the packet is correct, and the number of inverted bits. Adder Packet Statistics together with the delay unit Previous Totals accumulates an event vector of five counters.

Pay attention to the "package is correct" class: it requires that the information bits have not changed and the syndrome is zero. An error in only the verification bits gives a non—zero syndrome and is counted as detected - the receiver cannot distinguish it from an error in the data.

Block parameters are variables of the workspace crc_n, crc_k, crc_p, crc_seed, crc_packet_limit and crc_error_limit. They are set by the script code before each launch. The model can be reproducibly recreated using a script. resources/julia/build_model.jl.

Block Parameters

Let's connect the model management functions from resources/julia/crc_runtime.jl and the directory of polynomials, open the model and output the main parameters of the key blocks with the command engee.get_param. All paths are built from the script folder via @__DIR__.

In [ ]:
using JSON3, Printf, Plots
gr()

resources_dir = joinpath(@__DIR__, "resources")
include(joinpath(resources_dir, "julia", "crc_runtime.jl"))
include(joinpath(resources_dir, "julia", "polynomials.jl"))
crc_open_model()

show_params = Dict(
    "Payload" => ["SamplesPerFrame", "ProbabilityOfZero", "InitialSeed", "SampleTime"],
    "CRC Encoder" => ["GeneratorPolynomial", "InitialStates", "DirectMethod", "ReflectInputBytes", "FinalXOR"],
    "Channel Noise" => ["SamplesPerFrame", "ProbabilityOfZero", "InitialSeed", "SampleTime"],
    "Binary Symmetric Channel" => ["Operator"],
    "CRC Detector" => ["GeneratorPolynomial", "InitialStates", "DirectMethod"],
    "Data Changed" => ["Operator"], "Any Bit Changed" => ["Operator", "Inputs"],
    "Undetected" => ["Operator"], "Correct" => ["Operator"],
    "Previous Totals" => ["InitialCondition", "SampleTime"],
    "Stop Limits" => ["Value"], "Limit Reached" => ["Operator"])
for b in ["Payload", "CRC Encoder", "Channel Noise", "Binary Symmetric Channel", "CRC Detector", "Data Changed",
          "Any Bit Changed", "Undetected", "Correct", "Previous Totals", "Stop Limits", "Limit Reached"]
    prm = engee.get_param("crc_error_detection/" * b)
    println(rpad(b, 26), prm["BlockPath"])
    println(" "^26, join(["$k = $(prm[k])" for k in show_params[b]], "; "))
end
Payload                   /Communication Systems/Sources/Random Data Sources/Bernoulli Binary Generator
                          SamplesPerFrame = crc_k; ProbabilityOfZero = 0.5; InitialSeed = crc_seed; SampleTime = 1/crc_k
CRC Encoder               /Communication Systems/Error Detection And Correction/CRC/General CRC Generator
                          GeneratorPolynomial = [5 3 0]; InitialStates = 0; DirectMethod = false; ReflectInputBytes = false; FinalXOR = 0
Channel Noise             /Communication Systems/Sources/Random Data Sources/Bernoulli Binary Generator
                          SamplesPerFrame = crc_n; ProbabilityOfZero = 1 - crc_p; InitialSeed = crc_seed + 1; SampleTime = 1/crc_n
Binary Symmetric Channel  /Basic/Logic and Bit Operations/Logical Operator
                          Operator = XOR
CRC Detector              /Communication Systems/Error Detection And Correction/CRC/General CRC Syndrome Detector
                          GeneratorPolynomial = [5 3 0]; InitialStates = 0; DirectMethod = false
Data Changed              /Basic/Logic and Bit Operations/Logical Operator
                          Operator = XOR
Any Bit Changed           /Basic/Logic and Bit Operations/Logical Operator
                          Operator = OR; Inputs = 1
Undetected                /Basic/Logic and Bit Operations/Logical Operator
                          Operator = AND
Correct                   /Basic/Logic and Bit Operations/Logical Operator
                          Operator = NOR
Previous Totals           /Basic/Discrete/Unit Delay
                          InitialCondition = zeros(5); SampleTime = 1
Stop Limits               /Basic/Sources/Constant
                          Value = [crc_packet_limit, Inf, crc_error_limit, Inf, Inf]
Limit Reached             /Basic/Logic and Bit Operations/Relational Operator
                          Operator = >=

The parameters are set by workspace variables. The step of the sources is equal to and by one bit, therefore, per model step, each source outputs a frame from or bit. The CRC parameters correspond to the classical scheme: zero initial state, without direct method, bit reflection and final XOR. The stop threshold for the second, fourth, and fifth counters is in other words, they do not affect the stop.

The Handbook of Polynomials

File polynomials.jl It contains a table of generating polynomials of cyclic Bose—Chowdhury—Hockingham codes (BCH). For each row, a list of degrees in the model format and the supported feature are calculated: whether the CRC Engee blocks support the polynomial. Let's print the table.

In [ ]:
println(rpad("n", 5), rpad("k", 5), rpad("t", 5), rpad("Engee blocks", 13), "polynomial (degrees)")
for x in crc_presets
    println(rpad(x.n, 5), rpad(x.k, 5), rpad(x.t, 5), rpad(x.supported ? "yes" : "no", 13), x.poly)
end
@printf("Поддерживается %d из %d полиномов\n", count(x -> x.supported, crc_presets), length(crc_presets))
n    k    t    блоки Engee  полином (степени)
7    4    1    да           [3, 1, 0]
15   11   1    да           [4, 1, 0]
15   7    2    да           [8, 7, 6, 4, 0]
15   5    3    да           [10, 8, 5, 4, 2, 1, 0]
31   26   1    да           [5, 2, 0]
31   21   2    да           [10, 9, 8, 6, 5, 3, 0]
31   16   3    да           [15, 11, 10, 9, 8, 7, 5, 3, 2, 1, 0]
31   11   5    да           [20, 18, 17, 13, 10, 9, 7, 6, 4, 2, 0]
31   6    7    да           [25, 24, 21, 19, 18, 16, 15, 14, 13, 11, 9, 5, 2, 0]
63   57   1    да           [6, 1, 0]
63   51   2    да           [12, 10, 8, 5, 4, 3, 0]
63   45   3    да           [18, 17, 16, 15, 9, 7, 6, 3, 2, 1, 0]
63   39   4    да           [24, 23, 22, 20, 19, 17, 16, 13, 10, 9, 8, 6, 5, 4, 2, 1, 0]
63   36   5    да           [27, 22, 21, 19, 18, 17, 15, 8, 4, 1, 0]
63   30   6    нет          [33, 32, 30, 29, 28, 27, 26, 23, 22, 20, 15, 14, 13, 11, 9, 8, 6, 5, 2, 1, 0]
63   24   7    нет          [39, 38, 37, 36, 34, 33, 31, 28, 27, 25, 23, 22, 17, 11, 8, 5, 0]
63   18   10   нет          [45, 43, 42, 41, 40, 37, 36, 31, 29, 28, 26, 24, 21, 19, 16, 15, 14, 12, 9, 8, 7, 6, 4, 2, 0]
63   16   11   нет          [47, 46, 43, 42, 40, 39, 36, 33, 32, 27, 25, 24, 23, 22, 20, 19, 18, 16, 13, 12, 11, 9, 8, 5, 3, 1, 0]
63   10   13   нет          [53, 50, 49, 48, 46, 44, 43, 40, 37, 34, 33, 29, 27, 26, 24, 22, 20, 19, 18, 16, 14, 12, 6, 5, 3, 2, 0]
63   7    15   нет          [56, 54, 52, 49, 48, 45, 41, 38, 36, 35, 33, 32, 28, 27, 26, 24, 19, 18, 16, 14, 13, 12, 9, 8, 7, 6, 4, 3, 2, 1, 0]
Поддерживается 14 из 20 полиномов

Column — the number of errors that the corresponding BCH code would correct. The model only detects errors, so provided as a reference. All length codes are available , and and five length codes . Six length codes with polynomials of degree from before CRC library blocks are not supported: their degree is greater .

Checking the model

File crc_runtime.jl It contains six functions:

  • crc_validate checks the parameters of the experience: block length, probability, list of degrees of the polynomial, limits and seed;
  • crc_poly_literal writes the vector of degrees of a polynomial as a literal for CRC blocks;
  • crc_open_model loads the model if it is not open yet.;
  • crc_signal finds the recorded block signal in the simulation results;
  • crc_run_json accepts parameters as a JSON string, writes them to workspace variables and CRC block parameters, and runs the model with the command engee.run and returns the total counters as JSON;
  • crc_reference_encode — independent CRC encoder: dividing polynomials "into columns" to check the General CRC Generator block.

The JSON format was chosen because the web application uses the same function. Let's launch a control experiment: , , a polynomial [5, 3, 0], packages.

In [ ]:
crc_example_config = Dict("n" => 19, "p" => 0.1, "poly" => [5, 3, 0],
                          "blocksToStop" => 2000, "errorsToStop" => 2000, "seed" => 12345)
crc_example_result = JSON3.read(crc_run_json(JSON3.write(crc_example_config)))
@printf("Всего %d: верно %d, обнаружено %d, не обнаружено %d; битовых ошибок %d; %s; %.1f с\n",
        crc_example_result.packets, crc_example_result.correct, crc_example_result.detected,
        crc_example_result.undetected, crc_example_result.bit_errors, crc_example_result.stop_reason,
        crc_example_result.seconds)
@assert crc_example_result.correct + crc_example_result.detected + crc_example_result.undetected == crc_example_result.packets
Всего 2000: верно 270, обнаружено 1720, не обнаружено 10; битовых ошибок 3779; Достигнут предел пакетов; 31.0 с

Behind packages at correct accepted packages, erroneous packages were detected and skipped. This is the number of packets, not the bits: the channel has been inverted. bit from That is , under expected conditions . The percentage of correct packages coincides with the theoretical . The sum of the three classes is equal to the number of transmitted packets. This check is performed by @assert.

Now let's perform a set of control experiments. File verify_model.jl runs the model in six experiments with a pre-known answer and verifies that invalid parameters are rejected. Among the checks is a comparison of the output of the General CRC Generator block with an independent encoder crc_reference_encode on real packages of the model and checking that any single error gives a non-zero syndrome. The result is written in resources/data/verification.json.

In [ ]:
verification = JSON3.read(include(joinpath(resources_dir, "julia", "verify_model.jl")))
println("All checks have been completed: ", verification.passed)
for t in verification.tests
    if haskey(t, :result)
        r = t.result
        @printf("  %-48s n=%-3d p=%-4g пакетов %-5d верно %-5d обн. %-5d не обн. %d\n",
                t.case, r.n, r.p, r.packets, r.correct, r.detected, r.undetected)
    else
        @printf("  %-48s кадров %d, одиночных ошибок %d\n", t.case, t.frames, t.single_bit_cases)
    end
end
Все проверки пройдены: true
  no noise                                         n=19  p=0    пакетов 1000  верно 1000  обн. 0     не обн. 0
  undetected limit                                 n=7   p=1    пакетов 5     верно 0     обн. 0     не обн. 5
  all inverted detected                            n=19  p=1    пакетов 31    верно 0     обн. 31    не обн. 0
  repeatable noise                                 n=19  p=0.1  пакетов 2000  верно 270   обн. 1720  не обн. 10
  CRC Generator block versus independent polynomial division кадров 32, одиночных ошибок 19
  degree 27 source polynomial                      n=63  p=0    пакетов 64    верно 64    обн. 0     не обн. 0

All checks have been completed:

  • everything is quiet The packages were accepted correctly;
  • for the code inversion of all the bits turns a code word into another code word: a word of seven units is divided into . CRC does not see such an error, and the Stop Simulation block stops the model exactly after undetected errors;
  • for the length the same inversion is always found;
  • the same seed reproduces all counters;
  • the output of the General CRC Generator block coincided with the independent encoder on all packages, that's all single errors give a non-zero syndrome. This means that the library unit calculates the CRC exactly according to the formula from the theoretical part.;
  • the polynomial of the largest supported degree from the table, it works without errors, and the polynomial of degree rejected before the model is launched.

Comparison with theory

Calculate the exact weight range of the code with a polynomial . To do this, we will encode everything with a reference encoder. Let's find the information words and calculate the weights of the code words. Function theory Based on the spectrum, it calculates the probabilities of three classes of packages.

In [ ]:
poly = [5, 3, 0]; n_c, k_c = 19, 14
A = zeros(Int, n_c + 1)                          # A[w+1] is the number of codewords of weight w
for m in 0:2^k_c-1
    payload = [(m >> (k_c - i)) & 1 for i in 1:k_c]
    A[count(==(1), crc_reference_encode(payload, poly))+1] += 1
end
println("Weight spectrum A_w (w: number of words): ", join(["$(w):$(A[w+1])" for w in 0:n_c if A[w+1] > 0], "  "))
@printf("Минимальный ненулевой вес d_min = %d\n", findfirst(>(0), A[2:end]))

theory(p) = begin
    pc = (1 - p)^n_c
    pu = sum(A[w+1] * p^w * (1 - p)^(n_c - w) for w in 1:n_c)
    (correct = pc, undetected = pu, detected = 1 - pc - pu)
end
Весовой спектр A_w (w: число слов): 0:1  3:32  4:132  5:368  6:840  7:1544  8:2358  9:2952  10:2896  11:2288  12:1572  13:896  14:360  15:104  16:33  17:8
Минимальный ненулевой вес d_min = 3
Out[0]:
theory (generic function with 1 method)

Minimum weight of a non-zero codeword Therefore, CRC is guaranteed to detect any single and double errors. There are many words of odd weight: a polynomial does not contain a multiplier therefore, unlike many standard CRCs, such as CRC-16, it does not detect all errors of odd multiplicity. Any error packet up to in length the bit is detected: such an error vector is not divisible by .

Let's run a model for the probabilities of bit inversion from before , by packages at each point, and compare the shares of the three classes of packages with the theory.

In [ ]:
ps = [0.001, 0.003, 0.01, 0.03, 0.1, 0.2, 0.3, 0.5]
sim = Dict{Float64, Any}()
for p in ps
    cfg = Dict("n" => 19, "p" => p, "poly" => poly, "blocksToStop" => 20000, "errorsToStop" => 1_000_000, "seed" => 777)
    sim[p] = JSON3.read(crc_run_json(JSON3.write(cfg)))
    th = theory(p); r = sim[p]
    @printf("p = %-5g верно %.4f (теор. %.4f)  обн. %.4f (%.4f)  не обн. %.2e (%.2e)\n", p,
            r.correct / r.packets, th.correct, r.detected / r.packets, th.detected,
            r.undetected / r.packets, th.undetected)
end
p = 0.001 верно 0.9814 (теор. 0.9812)  обн. 0.0186 (0.0188)  не обн. 0.00e+00 (3.16e-08)
p = 0.003 верно 0.9436 (теор. 0.9445)  обн. 0.0563 (0.0555)  не обн. 0.00e+00 (8.34e-07)
p = 0.01  верно 0.8286 (теор. 0.8262)  обн. 0.1714 (0.1738)  не обн. 0.00e+00 (2.84e-05)
p = 0.03  верно 0.5623 (теор. 0.5606)  обн. 0.4370 (0.4388)  не обн. 6.50e-04 (6.05e-04)
p = 0.1   верно 0.1323 (теор. 0.1351)  обн. 0.8588 (0.8552)  не обн. 8.85e-03 (9.75e-03)
p = 0.2   верно 0.0129 (теор. 0.0144)  обн. 0.9613 (0.9607)  не обн. 2.57e-02 (2.49e-02)
p = 0.3   верно 0.0009 (теор. 0.0011)  обн. 0.9682 (0.9685)  не обн. 3.08e-02 (3.03e-02)
p = 0.5   верно 0.0000 (теор. 0.0000)  обн. 0.9682 (0.9688)  не обн. 3.18e-02 (3.12e-02)

The proportions of correct and detected packets match the theory up to the second or third digit in the entire range. Undetected errors are a rare event, and with small the model has not registered any. To understand whether this is consistent with theory, we need to estimate the statistical error.

Let's build two panels: the proportion of correct and detected packages on a regular scale, then the proportion of undetected errors on a logarithmic scale. For each non-zero estimate, we will show a 95% Wilson confidence interval. For a share its boundaries

If the model has not registered the event, instead of the zero invisible on the logarithmic scale, we will show the upper 95% probability limit with a triangle. Such a triangle is not a measured fraction. A horizontal line Don't draw: value applies only to , rather than the entire range .

In [ ]:
# The theoretical probabilities are calculated from the exact weight spectrum A_w.
pgrid = 10 .^ range(-3, log10(0.5); length = 200)

function wilson95(k, n)
    z = 1.959963984540054
    q = k / n
    den = 1 + z^2 / n
    mid = (q + z^2 / (2n)) / den
    half = z * sqrt(q * (1 - q) / n + z^2 / (4n^2)) / den
    (max(0.0, mid - half), min(1.0, mid + half))
end

main_plot = plot(pgrid, [theory(p).correct for p in pgrid];
    color = :seagreen, lw = 2, label = "That's right, theory", xscale = :log10,
    ylims = (-0.03, 1.05), ylabel = "Share of packages",
    title = "CRC x^5+x^3+1, n =19: model and exact theory",
    legend = :outertopright, left_margin = 5Plots.mm)
plot!(main_plot, pgrid, [theory(p).detected for p in pgrid];
    color = :royalblue, lw = 2, label = "discovered, theory")
scatter!(main_plot, ps, [sim[p].correct / sim[p].packets for p in ps];
    color = :seagreen, marker = :circle, ms = 6, label = "That's right, the model")
scatter!(main_plot, ps, [sim[p].detected / sim[p].packets for p in ps];
    color = :royalblue, marker = :square, ms = 6, label = "detected, the model")

rare_plot = plot(pgrid, [theory(p).undetected for p in pgrid];
    color = :orangered, lw = 2, label = "not detected, theory",
    xscale = :log10, yscale = :log10, ylims = (1e-8, 0.1),
    xlabel = "Probability of inversion of the p bit", ylabel = "Share of packages",
    legend = :outertopright, left_margin = 5Plots.mm)
nonzero_ps = [p for p in ps if sim[p].undetected > 0]
zero_ps = [p for p in ps if sim[p].undetected == 0]
for (j, p) in enumerate(nonzero_ps)
    r = sim[p]
    low, high = wilson95(r.undetected, r.packets)
    plot!(rare_plot, [p, p], [low, high]; color = :orangered, lw = 2,
        label = j == 1 ? "95% интервал Уилсона" : "")
end
scatter!(rare_plot, nonzero_ps,
    [sim[p].undetected / sim[p].packets for p in nonzero_ps];
    color = :orangered, marker = :diamond, ms = 6, label = "not detected, the model")
scatter!(rare_plot, zero_ps,
    [wilson95(0, sim[p].packets)[2] for p in zero_ps];
    color = :orangered, marker = :dtriangle, ms = 7,
    label = "0 events: upper 95% limit")

println("A 95% interval is expected for the share of events within")
for p in ps
    r = sim[p]
    low, high = wilson95(r.undetected, r.packets)
    expected = r.packets * theory(p).undetected
    @printf("%-7g %6d    %7.3f    [%.3g, %.3g]       %s\n",
        p, r.undetected, expected, low, high,
        low <= theory(p).undetected <= high ? "да" : "нет")
end
@assert all(p -> begin
    r = sim[p]
    low, high = wilson95(r.undetected, r.packets)
    low <= theory(p).undetected <= high
end, ps)

plot(main_plot, rare_plot; layout = (2, 1), size = (1050, 760))
p       событий  ожидается  95% интервал для доли        теория внутри
0.001        0      0.001    [0, 0.000192]       да
0.003        0      0.017    [0, 0.000192]       да
0.01         0      0.568    [0, 0.000192]       да
0.03        13     12.094    [0.00038, 0.00111]       да
0.1        177    195.099    [0.00764, 0.0102]       да
0.2        515    497.190    [0.0236, 0.028]       да
0.3        617    606.646    [0.0285, 0.0333]       да
0.5        636    624.962    [0.0295, 0.0343]       да
Out[0]:
No description has been provided for this image

The model is consistent with the exact theory within the statistical margin of error. The upper panel shows the matching of the correct and detected packets. In the lower panel, the intervals around the non-zero estimates contain a theoretical curve, and the table confirms this for all eight points.

By , and the exact theory expects accordingly about , and an undetected packet from . The probability of getting zero events at compose . Therefore, zero counters do not contradict the theory: for such a lot more packages are needed.

By the model gave against theoretical ones (about expected events). The uncertainty interval includes the theoretical value. By the exact value , the model gave about .

Web application based on Genie Framework

Genie Framework — a set of packages for creating web applications:

  • Genie.jl — a web server with query routing, HTML and JSON output;
  • Stipple.jl — a reactive interface that binds script variables to page elements;
  • GenieFramework.jl — a common package that connects everything at once and gives a macro @genietools.

Engee launches Genie applications in the cloud: team engee.genie.start(папка) lifts the server from the file app.jl in the specified folder and returns the page address. The application code is executed in a separate process. To execute the command in the main Engee session where the model is loaded, use the function engee.genie.eval(строка_кода).

The application of this example is in the folder resources/CRCApp:

  1. index.html — a page with experience parameter fields: block size, error probability, polynomial, stop limits, and seed. The buttons launch the experience, clear the history, and upload the CSV. The history is stored in sessionStorage browser tabs.
  2. The route / in app.jl gives this page as it is. The interface is written in pure JavaScript, so the Stipple reactive model is not needed here.
  3. The route POST /api/run accepts parameters in JSON and checks each field with a function validate_request. Then he calls engee.genie.eval("crc_run_json(...)"): the model is launched in the main session, and the counters are returned to the page.
  4. Blocking ReentrantLock it does not allow you to start the second experience while the first one is running.
  5. User input is never executed as code: only the verified JSON string is passed to the main session as a string literal.

Each row of the table in the application is the result of a real model launch. crc_error_detection.engee, not an analytical calculation. Let's launch the app. File start.jl connects control functions, opens the model, and calls engee.genie.start.

In [ ]:
include(joinpath(resources_dir, "julia", "start.jl"))
app_url = match(r"https://[^\s']+?/genie/CRCApp/", repr(crc_app_status))
println("The application is running: ", isnothing(app_url) ? crc_app_status : app_url.match)
Приложение запущено: https://engee.com/prod/user/demo54365636-yurev/genie/CRCApp/

The cell displays the address of the application in the form https://engee.com/prod/user/<имя_пользователя>/genie/CRCApp/. Open it in a browser. This is what the shape of the experience parameters looks like.:

crc_app_parameters.png

After starting the model, the result appears in the table. The picture below shows the experience with the initial parameters.: packages at . The percentage of correct packages coincides with the theoretical . After launch, the model remains open for further experiments in the script and in the application.

crc_app_results.png

The last cell returns a summary for the point .

In [ ]:
th = theory(0.1); r = sim[0.1]
string("CRC ok; p=0.1: correct=", round(r.correct / r.packets, digits = 4), " (theory ", round(th.correct, digits = 4),
       "), undetected=", round(r.undetected / r.packets, sigdigits = 3), " (theory ", round(th.undetected, sigdigits = 3), ")")
Out[0]:
"CRC ok; p=0.1: correct=0.1324 (theory 0.1351), undetected=0.00885 (theory 0.00975)"

Conclusion

We investigated error detection using CRC on the Engee model from library blocks and compared the simulation results with the exact theory. The code with a polynomial and the length of the package the bit is guaranteed to detect single and double errors, since its minimum weight is , as well as error packets up to in length bit. The proportions of correct, detected, and undetected packets coincided with the theory calculated from the weight spectrum of the code, over the entire range from before . By CRC skips about packets, and in a completely noisy channel — . The probability of missing an error is determined by the degree of the polynomial and its weight spectrum, therefore, higher degree polynomials are used in reliable systems.

This approach — a model, software checks, comparison with theory, and an interactive application — is useful for laboratory work and for the engineering evaluation of error-detecting systems.