Files
Presentation-TIPE/main.typ
2026-05-31 21:11:59 +02:00

2170 lines
59 KiB
Typst
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#import "composants.typ": *
#import "@preview/touying:0.5.2": *
#import "code_slides.typ": generer_slides_code
#set page(
paper: "presentation-4-3",
margin: 0cm,
)
// Font
#set text(
font: "New Computer Modern",
size: 20pt,
fill: black,
)
#set math.mat(delim: "[")
// Page de garde
#slide[
#place(center + horizon)[
#graphe_tanner_fond(1cm, 1.5)
]
#v(1fr)
#align(center + horizon)[
#pad(x: 2cm)[
#text(size: 3em, weight: "bold", fill: black)[#titre]
#text(size: 1.2em, weight: "bold", fill: black)[#auteur]
#h(0.5em)
#text(size: 1.2em, fill: black)[#numero]
#text(size: 0.95em, fill: black)[#annee]
]
]
#v(1fr)
]
#myslide("Introduction : Utilisation")[
#align(center + horizon)[
#grid(
columns: (1.5fr, 1.5fr),
gutter: 1.5cm,
align: center + horizon,
[
#box(width: 80%)[
#stack(
dir: ttb,
spacing: -0.5em,
image("src/athena_fidus_no_text.jpg", width: 100%),
align(left)[
#box(
inset: (x: 5pt, y: 0pt),
text(size: 0.45em, fill: white)[* https://gallery.ariane.group *],
)
],
)
]
#v(0.5em)
Athena-Fidus
],
[
#box(width: 80%)[
#stack(
dir: ttb,
spacing: -0.5em,
image("src/runcamfpv2.png", width: 100%),
align(left)[
#box(
inset: (x: 5pt, y: 0pt),
text(size: 0.45em, fill: white)[* https://www.runcam.com/ *],
)
],
)
]
#v(0.5em)
Module OpenIPC
],
)
]
]
#myslide("Introduction : Communication Numérique")[
#place(center + horizon, dy: 8cm)[
#canal_shannon_intro()
]
]
#myslide("Problématique")[
#place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
#place(center + horizon, dy: 7.7cm)[
#block(width: 100%)[
#text(size: 1.2em, weight: "bold", fill: black)[
Comment utiliser les codes LDPC pour garantir la fiabilité d'une transmission en présence de bruit ?
]
]
]
]
#myslide("Plan")[
#grid(
columns: (auto, 1fr),
align: horizon,
place(center + horizon, dx: 11.8cm, dy: 0.7cm)[#decor_matrice_etoilee()],
design_plan((
[Introduction],
[Codes linéaires],
[LDPC],
[Codage],
[Décodage],
[Analyse],
)),
)
]
#myslide("Définition : Codes Linéaires en Bloc")[
#definition(titre: [Code $display((n,k) in NN^2)$])[
$cal(C)$ sous-espace vectoriel de dimension $k$ de $FF_2^n$
]
#[
#set text(size: 1.1em)
- $k$ : longueur du message original
- $n$ : longueur du mot de code
- $m = n - k$ : nombre de bits de parités
]
#definition(titre: "Encodage")[
$Phi : FF_2^k & -> FF_2^n in cal(L)(FF_2^k, FF_2^n)$
]
#v(-1.3em)
#align(center + horizon)[
#plongement_schema()
]
]
#myslide("Définition : Matrice Génératrice")[
#v(0.80cm)
#definition(titre: "Matrice Génératrice")[
$G in cal(M)_(k,n)(FF_2)$ dont les lignes sont une base de $cal(C)$
]
#definition(titre: "Encodage")[
Pour un message $u in FF_2^k$ le mot de code $c in cal(C)$ est :
$
c = Phi(u) = u G
$
]
#definition(titre: "Forme systématique")[
// TODO : changer [I_k | P] en un graphique jolie avec I et P dans un carré coloré
$
#dessiner_matrice($G =$, ((texte: $I_k$, largeur: 2.2, fond: gray.lighten(75%)), (texte: $P$, largeur: 2.2, fond: gray.lighten(75%))))
// G = mat(
// I_k, P;
// augment: #1,
// delim: "[",
// )
$
]
#[
#set text(size: 1.2em)
// - Pour $u in FF_2^k, space display(u dot.o G = mat(u, u dot.o P; augment: #1, delim: "[",))$
// - Pour $u in FF_2^k, space #dessiner_matrice($u dot.o G =$, (
// (texte: $u$, largeur: 1.1, fond: gray.lighten(75%)),
// (texte: $u dot.o P$, largeur: 3.0, fond: gray.lighten(75%)),
// ))$
- $P in cal(M)_(k ,n-k)(FF_2)$ matrice de parité\
]
]
#myslide("Définition : Matrice de Contrôle")[
#v(1.1cm)
#definition(titre: "Matrice de Contrôle")[
// $H = mat(
// P^top, I_(n-k);
// augment: #1,
// delim: "[",
// )
$
#dessiner_matrice($H =$, (
(texte: $P^top$, largeur: 2.2, fond: gray.lighten(75%)),
(texte: $I_(n-k)$, largeur: 2.2, fond: gray.lighten(75%)),
))
$
]
#[
#set text(size: 1.2em)
// - $cal(C) = ker(H) = {v in FF_2^n | H dot.o v^top = 0}$
- $cal(C) = ker(H)$ -- $H c^top = 0$ $=>$ $c in cal(C)$
// - $display(G dot.o H^top = 0)$
]
#definition(titre: "Syndrome")[
Pour un vecteur reçu $r = c + e, space s in FF_2^(n - k)$
$
s = H r^top = H c^top + H e^top = 0 + H e^top
$
]
#[
#set text(size: 1.2em)
- $s = 0 => r in cal(C)$
- $s != 0$ donne la signature de l'erreur $e$
]
// Possible décodage par syndrome
]
#myslide("Exemple d'un code linéaire")[
#[
#set text(size: 1.1em)
#let col-u = blue
#let col-p = orange
#underline(offset: 3pt)[Exemple d'un code $(5,2)$]
- On choisit la matrice de parité $P$ :
$
P = #math.mat(
(text(fill: col-p)[1], text(fill: col-p)[1], text(fill: col-p)[0]),
(text(fill: col-p)[0], text(fill: col-p)[1], text(fill: col-p)[1]),
)
$
- Alors la matrice génératrice $G$ est :
$
G = #math.mat(
(text(fill: col-u)[1], text(fill: col-u)[0], text(fill: col-p)[1], text(fill: col-p)[1], text(fill: col-p)[0]),
(text(fill: col-u)[0], text(fill: col-u)[1], text(fill: col-p)[0], text(fill: col-p)[1], text(fill: col-p)[1]),
augment: 2,
)
$
- Message $display(u = #math.mat((text(fill: col-u)[1], text(fill: col-u)[1])))$
- Mot de code $c = u G$ :
#set text(size: 1.2em)
$
c = #math.mat((text(fill: col-u)[1], text(fill: col-u)[1]))
#math.mat(
(text(fill: col-u)[1], text(fill: col-u)[0], text(fill: col-p)[1], text(fill: col-p)[1], text(fill: col-p)[0]),
(text(fill: col-u)[0], text(fill: col-u)[1], text(fill: col-p)[0], text(fill: col-p)[1], text(fill: col-p)[1]),
augment: 2,
)
=
#math.mat((
text(fill: col-u)[1],
text(fill: col-u)[1],
text(fill: col-p)[1],
text(fill: col-p)[0],
text(fill: col-p)[1],
))
$
]
]
#myslide("Exemple d'un code linéaire")[
#let colp = orange
#let colu = blue
#let cole = red
#set math.mat(delim: "[")
#v(0.2cm)
#set text(size: 1.1em)
Structure systématique de $H$ :
#v(0.3em)
#align(center)[
#scale(108%)[
#dessiner_matrice($H =$, (
(texte: $P^top$, largeur: 2.0, fond: colp.lighten(90%)),
(texte: $I_3$, largeur: 3.0, fond: colu.lighten(90%)),
))
]
]
#v(0.4em)
Ainsi :
#v(-1.2em)
#align(center)[
#scale(110%)[
$
H = mat(
text(fill: colp, "1"), text(fill: colp, "0"), text(fill: colu, "1"), text(fill: colu, "0"), text(fill: colu, "0");
text(fill: colp, "1"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "1"), text(fill: colu, "0");
text(fill: colp, "0"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "0"), text(fill: colu, "1")
)
$
]
]
#v(0.55em)
Mot de code valide $c = (#text(fill: colp)[1], #text(fill: colp)[1], #text(fill: colu)[1], #text(fill: colu)[0], #text(fill: colu)[1])$ : #h(0.4em) $H c^top = mat(0; 0; 0)$ #h(0.5em) #text(fill: green.darken(20%))[]
#v(-0.5em)
Mot reçu avec #text(fill: cole)[une erreur] : $r = (#text(fill: colp)[1], #text(fill: colp)[1], text(fill: cole, "0"), #text(fill: colu)[0], #text(fill: colu)[1])$
#v(0.4em)
#align(center)[
#scale(110%)[
$
H r^top = mat(
text(fill: colp, "1"), text(fill: colp, "0"), text(fill: colu, "1"), text(fill: colu, "0"), text(fill: colu, "0");
text(fill: colp, "1"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "1"), text(fill: colu, "0");
text(fill: colp, "0"), text(fill: colp, "1"), text(fill: colu, "0"), text(fill: colu, "0"), text(fill: colu, "1")
) mat(
text(fill: colp, "1"), text(fill: colp, "1"), text(fill: cole, "0"), text(fill: colu, "0"), text(fill: colu, "1")
)^top = mat(1; 0; 0) != mat(0; 0; 0)
$
]
]
]
// A REMPLACER AVEC DE VRAI DONNE SUR DE VRAI CODE LDPC ET HAMMING PAR EXEMPLE
#myslide("Approcher la Limite de Shannon")[
// Faire un graphique waterfall comme sur les papiers de recherche...
#limite_shannon_graphique()
]
// #myslide("Redondance et limite théorique")[
// Graphique waterfall avec n = 100 et n = 64800 avec limite de Shannon, $display(R = k /n) < 1$, $m = n - k$
//
// Bande passante...
//
// Il existe $C$ pour un canal tel que pour $R < C$ on peut atteindre une probabilité d'erreur nulle.
// $=>$ gros bloc (moyenne du bruit aléatoire)
// ]
#myslide("Le Mur de la Complexité")[
#place(dy: 2.5cm)[
#set text(size: 19pt)
#definition(titre: [Décodage par Maximum de Vraisemblance], accent: black)[
Chercher le mot de code $bold(c) in cal(C)$ le plus probable sachant $bold(r)$ reçu :
$ hat(bold(c)) = arg min_(bold(c) in cal(C)) d_H (bold(r), bold(c)) $
]
#text(size: 21pt)[
- Équivalent à chercher l'erreur $bold(e)$ de poids minimal tel que $bold(H) bold(e)^top = bold(s)$.
]
#v(0.5em)
#definition(titre: "Le Problème du décodage par Syndrome")[
NP-Difficile et pour $H$ quelconque : $cal(O)(2^k)$
]
// - Pour $k=100$ bits, $2^100 approx 10^30$ opérations nécessaires.
]
]
#myslide("Définition des Codes LDPC")[
#definition(titre: [Codes LDPC Réguliers])[
#text(size: 22.97pt)[
Code linéaire en bloc avec une matrice de contrôle $bold(H)$ *clairsemée*.
#align(center)[
#grid(
columns: (auto, 3em, auto),
align: left,
[- Poids de Colonne *$w_c$*], [], [- Poids de Ligne *$w_r$*],
)
]
]
]
#place(center, dx: -290pt, dy: 60pt)[
#text(size: 13pt, style: "italic", fill: luma(160))[
$H in cal(M)_(15, 30)(FF_2)$, $display(R = 1/2)$
]
]
#v(-0.3cm)
#align(center)[
#scale(100%)[#hldpc()]
]
#v(0.0cm)
#grid(
columns: (1fr, 1fr),
column-gutter: 1.75em,
[#definition(titre: "Faible Densité", accent: black)[
#set align(center)
$w_c << m$ #h(1cm) $w_r << n$
]],
[#definition(titre: "Rendement")[
#set align(center)
$R = 1 - m / n$
]],
)
]
// #myslide("Définition des Codes LDPC")[
// #v(1.3cm)
// #definition(titre: [Codes LDPC Réguliers])[
// #text(size: 22.97pt)[
// Code linéaire en bloc avec une matrice de contrôle $bold(H)$ *clairsemée*.
// ]
// ]
// #v(0.3em)
// #align(center + horizon)[
// #grid(
// columns: (auto, 3em, auto),
// align: left,
// [• Poids de Colonne *$w_c$*], [], [• Poids de Ligne *$w_r$*],
// )
// ]
// #v(0.5em)
//
// // - Poids de Colonne *$w_c$*
// //
// // - Poids de Ligne *$w_r$*
// //
// // #v(0.5em)
//
// #definition(titre: "Conditions de Faible Densité", accent: black)[
// #set align(center)
// // $w_c << n - k$ #h(2cm) $w_r << n$
// $w_c << m$ #h(2cm) $w_r << n$
// ]
//
// #definition(titre: "Rendement")[
// // $ display(R = (n - op("rg")(H)) / n >= 1 - m / n) $
// $ display(R = 1 - m / n) $
// ]
// ]
//
// #myslide([Matrice de contrôle])[
// // #definition(titre: [Code LDPC $(6, 3)$])[
// // $m w_r = n w_c$ donc $H in cal(M)_(15, 30)(FF_2)$ et $display(R = 1 - m / n = 1/2)$
// // ]
// #definition(titre: [Code LDPC $(6, 3)$])[
// #v(8pt)
// $H in cal(M)_(15, 30)(FF_2)$ et $display(R = 1/2)$
// #v(5pt)
// ]
//
// #v(2cm)
//
// #place(center + horizon, dx: 0cm, dy: 7.3cm)[
// #scale(140%)[#hldpc()]
// ]
// ]
#myslide("De la Matrice aux Équations de Parité")[
#set text(size: 17pt)
#let v_space = 2.83cm
#align(center)[
#scale(115%)[
#grid(
columns: (auto, auto),
gutter: -15pt,
align: horizon,
[#hldpc_dual(row1: 0, row2: none)],
[
// #set math.mat(gap: 24.5pt)
#move(dy: 13.8pt)[
$
underbrace(
mat(r_0; r_1; #v(v_space);dots.v; r_29; delim: "["),
#text()[Mot reçu] r space in space FF_2^30
)
$
]
],
)
]
]
#v(0.5cm)
#set text(size: 1.2em)
// - Chaque ligne $j$ de $H$ définit une équation de parité $f_j$.
- $L_j$ définit une équation de parité $f_j$
- Pour $r$, on vérifie le syndrome : $H r^top = 0$
#v(0.2cm)
#definition(titre: [Équations de Parité], accent: orange)[
#set text(size: 1em)
#text(fill: orange)[$ f_0 : r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o r_24 plus.o r_29 = 0 $]
]
#v(0.3cm)
#text(size: 1.1em)[- Si $f_j = 1$, un nombre impair de bits a été inversé par le canal.]
]
// #myslide("L'Entrelacement des Contraintes")[
// #set text(size: 17pt)
//
// #align(center)[
// #move(dx: -1.2cm)[
// #scale(115%)[#hldpc_dual(row1: 0, row2: 14)]
// ]
// ]
//
// #v(1cm)
//
// #set text(size: 1.1em)
// - Chaque bit $r_i$ participe à $w_c = 3$ équations distinctes
//
// #v(1cm)
//
// // --- Étage 2 : Système et Décision ---
// #set align(center + horizon)
// #scale(115%)[
// #block(fill: gray.lighten(95%), inset: 10pt, stroke: 0.5pt + gray, radius: 4pt)[
// $
// cases(
// #text(fill: orange)[$r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o bold(r_24) plus.o r_29 &= 0$],
// #h(4.5cm) #text(size: 20pt)[$bold(dots.v)$],
// #text(fill: blue)[$r_1 plus.o r_3 plus.o r_12 plus.o bold(r_24) plus.o r_25 plus.o r_28 &= 0$]
// )
// $
// ]
// ]
//
// #v(1cm)
//
// #set align(left)
// #set text(size: 1.1em)
// - *$r_24$* : Surveillé par #text(fill: orange)[$f_0$] et #text(fill: blue)[$f_14$].
// - Si #text(fill: orange)[$f_0 = 1$] et #text(fill: blue)[$f_14 = 1$], $r_24$ est suspect
// ]
#myslide("L'Entrelacement des Contraintes")[
#v(0.7cm)
#set text(size: 20pt)
#align(center)[
#move(dx: -1.2cm)[
// #scale(110%)[#hldpc_triple(row1: 0, row2: 9, row3: 14)]
#scale(110%)[#hldpc_col24(row1: 0, row2: 9, row3: 14, col: 24)]
]
]
#v(0.5cm)
- Chaque bit $r_i$ participe à $w_c = 3$ équations distinctes :
#v(0.3cm)
#align(center)[
#block(fill: gray.lighten(95%), inset: 10pt, stroke: 0.5pt + gray, radius: 4pt)[
$
cases(
#text(fill: orange)[$r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o$ #text(fill: red)[$bold(r_24)$] #text(fill: orange)[$plus.o r_29 &= 0 quad (f_0)$]],
#text(fill: green.darken(20%))[$r_7 plus.o r_13 plus.o r_23 plus.o$ #text(fill: red)[$bold(r_24)$] #text(fill: green.darken(20%))[$plus.o r_25 plus.o r_27 &= 0 quad (f_9)$]],
#text(fill: blue)[$r_1 plus.o r_3 space thick$] #h(0.3pt) #text(fill: blue)[$plus.o r_12 plus.o$ #text(fill: red)[$bold(r_24)$] #text(fill: blue)[$plus.o r_25 plus.o r_28 &= 0 quad (f_14)$]]
)
$
]
]
#v(0.5cm)
- *$r_24$* : Surveillé simultanément par #text(fill: orange)[$f_0$], #text(fill: green.darken(20%))[$f_9$] et #text(fill: blue)[$f_14$].
- Si $forall #tricolor-j in {#text(fill: orange)[$0$], #text(fill: green.darken(20%))[$9$], #text(fill: blue)[$14$]}, space f_(#scale(80%)[#tricolor-j]) = 1$, alors le bit est considéré suspect.
]
#myslide("Graphe de Tanner : Définition")[
#set text(size: 1em)
#definition(titre: [Graphe de Tanner $cal(G)(bold(H))$])[
#set text(size: 0.9em)
Graphe bipartite $cal(G) = (#text(fill: blue)[$V$] union.sq #text(fill: orange)[$C$], A)$
:
#v(4pt)
$ (#text(fill: blue)[$v_j$], #text(fill: orange)[$c_i$]) in A space <==> space H_(i,j) = 1 $
]
#v(0.4em)
#set text(size: 1.1em)
#grid(
columns: (1.4fr, 1fr),
gutter: 1em,
[- $#text(fill: blue)[$V$] = {#text(fill: blue)[$v_0$], dots, #text(fill: blue)[$v_(n-1)$]}$ nœuds de *variable*],
[- $|A| = n dot #text(fill: blue)[$w_c$] = m dot #text(fill: orange)[$w_r$]$],
[
- $#text(fill: orange)[$C$] = {#text(fill: orange)[$c_0$], dots, #text(fill: orange)[$c_(m-1)$]}$ nœuds de *contrôle*
],
[- $H tilde.equiv cal(G)$],
[- $deg(#text(fill: blue)[$v_j$]) = #text(fill: blue)[$w_c$]$],
[- $deg(#text(fill: orange)[$c_i$]) = #text(fill: orange)[$w_r$]$],
)
#align(center)[
#scale(115%)[
#tanner_illustration()
]
#text(size: 0.8em, style: "italic", fill: gray.darken(20%))[
Exemple $n=4, space m=2$
]
]
]
#myslide("Construction du Graphe : Les Nœuds")[
#v(1.2em)
#align(center)[
#scale(100%)[
// #h_mini_tanner()
#grid(
columns: (1fr, 1fr),
gutter: 1em,
[#hldpc_dynamic(hl_cols: range(30), show_labels: false, h_show: true)],
[#hldpc_dynamic(hl_rows: range(15), show_labels: false, h_show: false)],
)
]
]
#v(3.5em)
#align(center)[
#scale(180%)[
#tanner_canvas(colored: true)
]
]
// #v(2.5em)
// #[
// #set text(size: 1.05em)
// - Chaque *colonne* $j$ de $bold(H)$ $arrow$ nœud de variable #text(fill: blue, weight: "bold")[$v_j in V$] \ #text(fill: blue)[$n = 30$] nœuds, représentés par des *cercles* ○
// - Chaque *ligne* $i$ de $bold(H)$ $arrow$ nœud de contrôle #text(fill: orange, weight: "bold")[$c_i in C$] \ #text(fill: orange)[$m = 15$] nœuds, représentés par des *carrés* □
// - Les arêtes seront déterminées par les $1$ de $bold(H)$ — étapes suivantes
// ]
]
#myslide("Construction du Graphe : Nœud de Contrôle")[
#v(1.2em)
#align(center)[
#move(dx: 12.6pt, dy: -1.3pt)[
#scale(100%)[
#hldpc_dynamic(hl_rows: (0,), show_labels: true, h_show: true)
]
]
]
#v(3.5em)
#align(center)[
#move(dx: 2.7pt, dy: -13.8pt)[
#scale(180%)[
#tanner_canvas(hl_row: 0)
]
]
]
// #v(0.4em)
// #[
// #set text(size: 1.05em)
// - Ligne #text(fill: orange, weight: "bold")[0] de $bold(H)$ : $bold(H)_(0,j) = 1$ pour $j in {7, 10, 15, 22, 24, 29}$
// - #text(fill: orange)[$c_0$] est relié à #text(fill: orange)[$v_7, v_10, v_15, v_22, v_24, v_29$] — chaque 1 crée une arête
// - $deg(c_0) = 6 = w_r$ — $c_0$ porte l'équation : #text(fill: orange)[$f_0 : r_7 plus.o r_10 plus.o r_15 plus.o r_22 plus.o r_24 plus.o r_29 = 0$]
// ]
]
#myslide("Construction du Graphe : Nœud de Variable")[
#v(1.2em)
#align(center)[
#scale(100%)[
#hldpc_dynamic(hl_cols: (10,), show_labels: true, h_show: true)
]
]
#v(3.5em)
#align(center)[
#move(dx: -0pt, dy: -39pt)[
#scale(180%)[
#tanner_canvas(hl_col: 10)
]
]
]
// #v(0.15em)
// #align(center)[
// #grid(
// columns: (auto, auto),
// column-gutter: 0.7cm,
// align: horizon,
// [#h_mini_tanner(hl_col: 10)], [#tanner_canvas(hl_col: 10)],
// )
// ]
// #[
// #set text(size: 1.05em)
// - Colonne #text(fill: blue, weight: "bold")[10] de $bold(H)$ : $bold(H)_(i,10) = 1$ pour $i in {0, 5, 11}$
// - #text(fill: blue)[$v_10$] est relié aux nœuds de contrôle #text(fill: blue)[$c_0, c_5, c_11$] — participe à *3 équations* de parité
// - $deg(v_10) = 3 = w_c$ — chaque bit est surveillé par $w_c$ contraintes indépendantes
// ]
]
#myslide("Graphe de Tanner Final")[
#move(dy: 4.5cm)[
#align(center + horizon)[
#tanner_canvas(scale: 0.91cm, show_all: true, colored: true, v_c_show: false)
]
#align(center)[
#grid(
columns: (1fr, 1fr),
gutter: 0.45cm,
block(
fill: blue.lighten(88%),
stroke: 0.5pt + blue.lighten(40%),
radius: 6pt,
inset: (x: 10pt, y: 8pt),
width: 100%,
)[
#place(dx: 47pt, dy: -0pt)[#scale(110%)[#icon_var]] #text(fill: blue, weight: "bold")[ Nœuds de variable] \
// $n = 30 quad deg = w_c = 3$
],
block(
fill: orange.lighten(88%),
stroke: 0.5pt + orange.lighten(40%),
radius: 6pt,
inset: (x: 10pt, y: 8pt),
width: 100%,
)[
#place(dx: 47pt, dy: -0pt)[#scale(110%)[#icon_chk]] #text(
fill: orange,
weight: "bold",
)[ Nœuds de contrôle] \
// $m = 15 quad deg = w_r = 6$
],
)
]
]
]
#let mot_valide = (
0,
0,
0,
0,
0,
1,
0,
1,
1,
0,
0,
0,
0,
0,
1,
0,
1,
0,
0,
0,
0,
0,
0,
0,
1,
1,
0,
1,
0,
0,
)
#myslide("La Contrainte de Somme Nulle")[
#set text(size: 18pt)
#definition(titre: "Vision Graphe", accent: green)[
Si $s = 0$ alors que chaque nœud de contrôle est localement satisfait
]
#v(1cm)
#align(center)[
#scale(150%)[
#tanner_status(
scale: 0.55cm,
v_values: mot_valide,
hl_v: (),
)
]
]
#v(1.2cm)
#set text(size: 20pt)
#align(center + horizon)[
Chaque #icon_chk calcule le xor de ses voisins #icon_var : $space display(f_i = xor.big_(j in cal(N)(c_i)) v_j)$
]
#v(1em)
#align(center)[
#move(dy: -10pt)[
#scale(150%)[
#zoom_contrainte(is_ok: true)
]
]
]
// #v(1em)
// #block(inset: 10pt, fill: rgb("#f0fdf4"), stroke: 1pt + rgb("#22c55e"), radius: 5pt)[
// Toutes les équations de parité sont vérifiées simultanément.
// ]
]
#myslide("La Contrainte de Somme Nulle")[
#set text(size: 18pt)
#definition(titre: "Détection d'Erreur", accent: red)[
Si un bit est inversé, toutes les contraintes associées sont à $1$
]
#v(1cm)
#align(center)[
#let idx_erreur = 10
#let mot_erronne = mot_valide.enumerate().map(p => if p.first() == idx_erreur { 1 } else { p.last() })
#scale(150%)[
#tanner_status(
scale: 0.55cm,
v_values: mot_erronne,
hl_v: (idx_erreur,),
highlight_edges: true,
)
]
]
#v(1.3cm)
#align(center)[
$0 plus.o bold(1) plus.o 0 = bold(1) arrow$ *Erreur détectée*
]
#v(1em)
#align(center)[
#move(dy: 11.5pt)[
#scale(150%)[
#zoom_contrainte(is_ok: false)
]
]
]
// #block(inset: 10pt, fill: rgb("#fef2f2"), stroke: 1pt + red, radius: 5pt)[
// *Principe du décodage :*
// Le bit $v_{10}$ est relié à *plusieurs alarmes* (arêtes rouges au premier plan). Il est le suspect idéal pour une correction par "bit-flipping".
// ]
]
#myslide("Encodage LDPC : Calcul de G")[
#set text(size: 18pt)
#definition(titre: "Encodage", accent: blue)[
Mot de code $bold(c)$ généré à partir d'un message $bold(u)$ :
$bold(c) = bold(u) bold(G)$\
// $bold(H) bold(G)^top = bold(0)$
]
#v(2em)
#align(center)[#scale(135%)[#paradoxe_densite_reel()]]
#v(2em)
#set text(size: 1em)
// - Forme Systématique : Par élimination de Gauss sur $bold(H)$, on obtient
- Forme Systématique
#v(1em)
#align(center)[
#schema_systematique()
]
#v(1em)
#set text(size: 1em)
- La matrice $bold(G)$ devient dense $=>$ encodage en $cal(O)(n^2)$
// #v(1em)
// #block(fill: rgb("#fef2f2"), stroke: 1pt + red, inset: 10pt, radius: 5pt)[
// *Défi Matériel :* Pour $n=64\,800$ (DVB-S2), $n^2$ est prohibitif. Solution : les codes *quasi-cycliques* (5G).
// ]
]
#myslide("Décodage : Bit-Flipping")[
#set text(size: 16pt)
#definition(titre: "Décision Stricte (Hard Decision)", accent: black)[
Algorithme *itératif* : les nœuds *échangent des bits* pour localiser les erreurs.
]
#v(0.2em)
#set text(size: 1em)
#set text(size: 16pt)
#definition(titre: "Message Passing", accent: blue)[
// L'information *circule le long des arêtes* du graphe de Tanner à chaque itération.
- #icon_var envoie son bit courant à ses voisins #icon_chk
- #icon_chk renvoie son *verdict de parité* ($0$ ou $1$)
]
#v(0.6em)
#set text(1em)
- Si $v_j$ participe à *trop d'équations non satisfaites* $=>$ on l'inverse.
#uncover(2)[
#grid(
columns: (1fr, 1fr, 1fr),
column-gutter: 10pt,
// On aligne tout par le bas (bottom) pour que les CN soient sur la même ligne
align: center + bottom,
[
#bf_step1_sending()
#v(-0.5em)
#text(style: "italic", fill: gray.darken(40%), size: 0.8em)[VN $arrow$ CN]
],
[
#bf_step2_verdict()
#v(-0.5em)
#text(style: "italic", fill: gray.darken(40%), size: 0.8em)[CN $arrow$ VN]
],
[
#bf_step3_flip()
#v(-0.5em)
#text(style: "italic", fill: gray.darken(40%), size: 0.8em)[Correction]
],
)
]
// #align(center + horizon)[
// #scale(100%)[#bp_hard_diagram()]
// #v(0.4em)
// #text(
// size: 0.72em,
// style: "italic",
// fill: gray.darken(20%),
// )[Échanges itératifs entre $V$ (cercles) et $C$ (carrés)]
// ]
]
#myslide("Bit-Flipping : Graphe de flot de contrôle")[
#set text(size: 17pt)
// #grid(
// columns: (1.25fr, 0.75fr),
// gutter: 0.8cm,
// align: top,
// [
// #step_box(1, orange)[
// *CN Update — Évaluation* \
// #set text(size: 0.88em)
// Chaque nœud de contrôle $c_i$ calcule sa parité :
// $
// f_i = xor.big_(j in cal(N)(c_i)) v_j in {0, 1}
// $
// Si $f_i = 1$ : *l'équation n'est pas satisfaite* $=>$ $c_i$ envoie le message "Erreur" à tous ses voisins.
// ]
//
// #v(0.45em)
//
// #step_box(2, blue)[
// *VN Update — Vote* \
// #set text(size: 0.88em)
// Chaque bit $v_j$ compte ses alarmes reçues $k_j$.
// Si $k_j$ dépasse le seuil (ex. *majorité*) :
// $
// v_j arrow.l 1 - v_j quad ("FLIP")
// $
// ]
//
// #v(0.45em)
//
// #step_box(3, green.darken(10%))[
// *Vérification — Syndrome* \
// #set text(size: 0.88em)
// On recalcule $bold(s) = bold(H) bold(r)^top$.
// - Si $bold(s) = bold(0)$ : *Succès*, on s'arrête.
// - Sinon : on recommence l'étape ① (jusqu'à `max_iter`).
// ]
// ],
// [
// #align(center)[
// #v(0.3em)
// #scale(96%)[#schema_boucle_bf()]
// ]
// ],
// )
#align(center + horizon)[
#move(dx: 2.5cm, dy: -0.5cm)[#scale(95%)[#schema_boucle_bf()]]
]
]
#let log10(x) = if x < 1e-9 { -9.0 } else { calc.log(x) / calc.log(10) }
#let raw_waterfall = (
(0.0, 0.158461, 0.281269),
(1.0, 0.130748, 0.247257),
(2.0, 0.104310, 0.205882),
(3.0, 0.079301, 0.151808),
(4.0, 0.056395, 0.063852),
(4.5, 0.046493, 0.023414),
(5.0, 0.037572, 0.004078),
(5.5, 0.029673, 0.000470),
(6.0, 0.022973, 0.000045),
(7.0, 0.012519, 0.000002),
(8.0, 0.006030, 0.000000001),
// On lisse la fin pour éviter l'effet "escalier"
(8.5, 0.004200, 0.0),
(9.0, 0.002800, 0.0),
(9.5, 0.001800, 0.0),
(10.0, 0.001100, 0.0),
)
#let pts_unc = raw_waterfall.map(r => (r.at(0), log10(r.at(1))))
#let pts_bf = raw_waterfall.map(r => (r.at(0), log10(r.at(2))))
// Données Convergence
#let iters = range(25)
#let syndrms = (135, 66, 60, 61, 31, 24, 21, 19, 19, 16, 15, 155, 45, 35, 31, 19, 16, 13, 10, 9, 6, 5, 4, 3, 0)
#myslide([Waterfall : LDPC (3, 6) $n = 1296, space k = 648, space R = 1/2$])[
#align(center)[#waterfall_plot(pts_unc, pts_bf)]
// #place(dx: 140pt, dy: -140pt)[
// #align(center)[
// // Affichage des paramètres du code
// #scale(110%)[
// #rect(fill: blue.lighten(95%), stroke: 0.5pt + blue.lighten(80%), radius: 4pt, inset: 10pt)[
// #set text(size: 18pt)
// *LDPC $(3, 6)$* #h(1cm)
// $n = 1296$, $k = 648$ #h(1cm)
// Rendement $R = 1/2$
// ]]
// ]
// ]
]
// #myslide([Résultats : Convergence syndrome])[
// #align(center)[#convergence_plot(iters, syndrms)]
// ]
// #myslide("Bit-Flipping : Analyse")[
#myslide("Bit-Flipping : Syndrome et Analyse")[
#place(center + horizon, dx: -2cm, dy: 8cm)[#convergence_plot(iters, syndrms)]
// TODO PARLER DU GIRTH 4 => MAUVAIS
#set text(size: 17pt)
#uncover(2)[
#place(dx: 13cm, dy: 1.0cm)[
#scale(100%)[
#definition(titre: "Avantages", accent: green.darken(10%), compact: true)[
#set text(size: 0.88em)
- *Complexité* : XOR et compteurs
- $cal(O)(n)$ par itération
// - *Matériel* : idéal FPGA/ASIC, massivement parallélisable
// - *Simplicité* : inventé par Gallager (1962)
]
]
#place(dx: -1.5cm, dy: 1.0cm)[
#scale(100%)[
#definition(titre: "Limite", accent: red, compact: true)[
#set text(size: 0.88em)
- Ignore la *confiance* du récepteur physique
- Un bit reçu à $0.51$ V est traité comme $0$
// - $=>$ Sous-optimal par rapport à la limite de Shannon
// #v(0.3em)
// #align(center)[
// #box(fill: rgb("#fff7ed"), stroke: (left: 3pt + orange), inset: (x: 8pt, y: 5pt))[
// #text(
// size: 0.82em,
// )[$arrow$ Nécessite le *Soft-Decision* (Belief Propagation) pour exploiter les niveaux de gris du signal]
// ]
// ]
]]
]]
]
]
#myslide("Décodage Soft : Le LLR")[
#set text(size: 17pt)
#definition(titre: "Signal", accent: blue.darken(10%))[
On reçoit une valeur $y_i$ (ex: $+4.5$V ou $-0.2$V).
]
#v(0.6em)
#definition(titre: "Log-Likelihood Ratio (LLR)", accent: black)[
$
L(v_i) = ln(display(frac(P(v_i = 0 | y_i), P(v_i = 1 | y_i))))
$
]
#v(2em)
#align(center)[
#move(dx: 1.05cm)[
#scale(160%)[#schema_llr_droite()]
]
]
#v(1.7em)
// Blocs de légende centrés
#align(center)[
#grid(
columns: (220pt, 220pt),
gutter: 1cm,
block(fill: blue.lighten(92%), stroke: 1pt + blue, radius: 6pt, inset: 12pt)[
#align(center)[
#text(fill: blue.darken(20%), weight: "bold")[Signe] \
#text(size: 0.85em)[Définit la valeur du bit]
]
],
block(fill: orange.lighten(90%), stroke: 1pt + orange, radius: 6pt, inset: 12pt)[
#align(center)[
#text(fill: orange.darken(20%), weight: "bold")[|Valeur|] \
#text(size: 0.85em)[Confiance dans la décision]
]
],
)
]
]
#myslide("Sum-Product : Belief Propagation")[
#set text(size: 17pt)
#v(-0.7em)
#definition(titre: "Décodage Optimal")[
Échange itératif de croyances (LLR) entre les nœuds du graphe
]
#definition(titre: "Information Extrinsèque")[
Exclure l'avis du destinataire pour éviter l'auto-influence
]
#block(
fill: orange.lighten(92%),
stroke: (left: 4pt + orange),
radius: 4pt,
inset: 18pt,
width: 100%,
)[
#set text(size: 1.2em)
*Mise à jour* \
#set text(size: 1.1em)
// $ tanh(m_(c arrow v) / 2) = product_(u != v) tanh(m_(u arrow c) / 2) $
$ m_(c arrow v) = 2 tanh^(-1) ( product_(u in cal(N)(c) \\ {v}) tanh(m_(u arrow c) / 2) ) $
]
#place(dx: 5.2cm, dy: -5.2cm)[#scale(170%)[#icon_chk]]
#block(
fill: blue.lighten(92%),
stroke: (left: 4pt + blue),
radius: 4pt,
inset: 18pt,
width: 100%,
)[
#set text(size: 1.2em)
*Mise à jour* \
#set text(size: 1.1em)
// $ m_(v arrow c) = L_"canal" + sum_(c' != c) m_(c' arrow v) $
// $ m_(v arrow c) = L_v^((0)) + sum_(c' in cal(N)(v) \\ {c}) m_(c' arrow v) $
$ m_(v arrow c) = L_(v"canal") + sum_(c' in cal(N)(v) \\ {c}) m_(c' arrow v) $
]
#place(dx: 5.2cm, dy: -3.85cm)[#scale(170%)[#icon_var]]
]
#myslide("Sum-Product")[
#set text(size: 16pt)
#place(left, dx: 1.5cm, dy: 0cm)[
#text(weight: "bold", size: 1.1em * 1.3)[Initialisation]
]
#place(left, dx: 1.5cm, dy: 2cm)[
#scale(130%)[
#grid(
columns: 1,
gutter: 0.2cm,
align: horizon,
schema_detailed_init(),
text(size: 0.9em)[$m_(v_j arrow c_i) = L_"canal"$],
)
]
]
#place(right, dx: -4.5cm, dy: 0cm)[
#text(weight: "bold", fill: orange, size: 1.1em * 1.3)[Échange]
]
#place(dx: 21cm, dy: 0.1cm)[#scale(170%)[#icon_chk]]
#place(right, dx: -1cm, dy: 2.7cm)[
#scale(130%)[
#schema_detailed_cn()
]
]
#place(left, dx: 1.5cm, dy: 9cm)[
#text(weight: "bold", fill: blue, size: 1.1em * 1.3)[Échange]
]
#place(dx: 5.4cm, dy: 9.15cm)[#scale(170%)[#icon_var]]
#place(left, dx: 1.5cm, dy: 11cm)[
#scale(130%)[
#schema_detailed_vn()
]
]
#place(right, dx: -3.5cm, dy: 9cm)[
#text(weight: "bold", fill: green, size: 1.1em * 1.3)[Décision Finale]
]
#place(right, dx: -1cm, dy: 11cm)[
#scale(145%)[
#schema_detailed_decision()
]
]
#place(center + horizon, dx: -0.9cm, dy: 8.2cm)[
#scale(115%)[
#cetz.canvas(length: 1cm, {
import cetz.draw: *
let col_iter = gray.darken(50%)
arc(
(0, 0),
radius: 1.8,
start: 150deg,
delta: -300deg,
stroke: (paint: col_iter, thickness: 2pt, cap: "round"),
mark: (end: "stealth", fill: col_iter, size: 0.3),
)
content((1.6, -1), [
#set text(fill: col_iter, weight: "bold", size: 0.8em)
#set align(center)
Itérations\
$i = 1, dots, I_(max)$
])
})
]
]
]
#myslide([Transmission d'image])[
#place(center, dy: -2cm)[
#scale(85%)[
#simulation_image_flow(
image("src/origine.png", width: 100%),
image("src/noisy.png", width: 100%),
image("src/decoded_R05_1-2.png", width: 100%),
image("src/decoded_R66_2-3.png", width: 100%),
image("src/decoded_R75_3-4.png", width: 100%),
img_w: 230pt,
gap_top: 7.0,
gap_bot: 2.0,
)
]
]
]
#myslide("Min-Sum")[
#set text(size: 15pt)
// #grid(
// columns: (1fr, 1fr),
// gutter: 0.5cm,
// definition(titre: "Avantage Matériel", accent: black)[
// - *Comparateurs* pour le minimum
// - *XOR* pour le produit des signes
// #v(0.21cm)
// ],
// definition(titre: "Mise à jour des CN", accent: orange)[
// #set text(size: 19pt)
// $
// m_(c arrow v_i) = product_(j != i) "sgn"(m_(v_j arrow c)) times min_(j != i) |m_(v_j arrow c)|
// $
// ],
// definition(titre: "Mise à jour des CN", accent: orange)[
// #set text(size: 19pt)
// $
// m_(c arrow v) = product_(u in cal(N)(c) \\ {v}) "sgn"(m_(u arrow c)) times min_(u in cal(N)(c) \\ {v}) |m_(u arrow c)|
// $
// ],
// definition(titre: "Mise à jour des CN", accent: orange)[
// #set text(size: 19pt)
// $
// m_(c arrow v) = & product_(u in cal(N)(c) \\ {v}) "sgn"(m_(u arrow c)) \
// & times min_(u in cal(N)(c) \\ {v}) |m_(u arrow c)|
// $
// ],
// )
#definition(titre: "Avantage Matériel", accent: black, titre_taille: 1.4em)[
- *Comparateurs* pour le minimum
- *XOR* pour le produit des signes
#v(0.21cm)
]
#definition(titre: "Mise à jour des CN", accent: orange, titre_taille: 1.4em)[
#set text(size: 24pt)
$
m_(c arrow v) = product_(u in cal(N)(c) \\ {v}) "sgn"(m_(u arrow c)) times min_(u in cal(N)(c) \\ {v}) |m_(u arrow c)|
$
]
#v(1.5em)
#align(center)[#scale(130%)[#schema_min_sum_complet()]]
#v(-0.5em)
#align(center)[
#text(size: 1.1em, fill: gray.darken(40%), style: "italic")[
Pour les VN : $display(m_(v arrow c) = L_"canal" + sum_(c' in cal(N)(v) \\ {c}) m_(c' arrow v))$
]
]
]
#let log10(x) = if x <= 1e-9 { -8.0 } else { calc.log(x) / calc.log(10) }
#let raw_data = (
(0.00, 1.2397e-1, 4.9986e-1, 1.1404e-1, 1.2470e-1),
(0.50, 1.1057e-1, 4.9993e-1, 9.5550e-2, 1.0485e-1),
(1.00, 9.7682e-2, 4.9978e-1, 7.5017e-2, 8.2555e-2),
(1.50, 8.4916e-2, 4.9993e-1, 4.6412e-2, 5.1824e-2),
(2.00, 7.3000e-2, 5.0008e-1, 1.1640e-2, 1.3790e-2),
(2.50, 6.1705e-2, 4.9987e-1, 4.0818e-4, 5.3380e-4),
(3.00, 5.1501e-2, 5.0020e-1, 4.8765e-5, 8.0247e-6),
(3.50, 4.1921e-2, 5.0005e-1, 0, 0),
(4.00, 3.3645e-2, 4.9959e-1, 0, 0),
(5.00, 1.9973e-2, 4.9890e-1, 0, 0),
(6.00, 1.0584e-2, 3.8048e-1, 0, 0),
(7.00, 4.8994e-3, 4.0660e-2, 0, 0),
(8.00, 1.8194e-3, 3.8519e-4, 0, 0),
(9.00, 5.6235e-4, 8.1790e-6, 0, 0),
(10.00, 1.3133e-4, 6.1728e-7, 0, 0),
(11.00, 2.5154e-5, 0, 0, 0),
)
#let pts_unc = raw_data.map(d => (d.at(0), log10(d.at(1))))
#let pts_bf = raw_data.map(d => (d.at(0), log10(d.at(2))))
#let pts_sp = raw_data.map(d => (d.at(0), log10(d.at(3))))
#let pts_ms = raw_data.map(d => (d.at(0), log10(d.at(4))))
#myslide([Waterfall : LDPC (3, 9) $n = 1296, space k = 864, space R = 2/3$])[
#align(center)[
// #rect(fill: gray.lighten(95%), radius: 4pt, inset: 8pt)[
// #set text(size: 16pt)
// Comparaison BER : *Bit-Flipping* (Décision dure) vs *Soft-Decisions* (SP & MS)
// ]
#comparison_waterfall_plot(pts_unc, pts_bf, pts_sp, pts_ms)
]
]
// #myslide("QC-LDPC")[
// #align(center + horizon)[
// #image("src/construction.jpg", width: 80%)
// ]
// ]
//
// #myslide("Test réel")[
// Transmission hackrf, test de diff de debit avec paquets
// Test de transmission d'image avec différent ldpc non opti et opti (H diff etc)
// #align(center + horizon)[
// #image("src/construction.jpg", width: 80%)
// ]
// ]
//
// #myslide("FPGA")[
// #align(center + horizon)[
// #image("src/construction.jpg", width: 80%)
// ]
// ]
#myslide("La Topologie de H : Le Girth")[
#set text(size: 20pt)
#definition(titre: "Définition : Le Girth (La Maille)", accent: blue)[
Longueur du plus court cycle dans le graphe de Tanner
]
#v(0.5em)
- Le girth est *pair*
- La valeur minimale est $g = 4$.
#v(1em)
#align(center)[
#block(fill: rgb("#f8fafc"), stroke: 1pt + blue.lighten(50%), inset: 10pt, radius: 5pt)[
Girth élevé $=>$ Meilleure diffusion de l'information.
]
]
#v(2.5em)
#align(center)[
#scale(140%)[
#grid(
columns: (1fr, 1fr),
gutter: -7cm,
[
#schema_girth_4(highlight: false)
#v(0.5em)
#text(size: 0.8em, style: "italic")[Graphe de Tanner]
],
[
#schema_girth_4(highlight: true)
#v(0.5em)
#text(size: 0.8em, fill: red, weight: "bold")[4-Cycle]
],
)
]
]
]
#myslide("Méthode de génération de H")[
#place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
#place(center + horizon, dy: 7cm)[
#block(width: 100%)[
#text(size: 1.5em, weight: "bold", fill: black)[
Gallager \
Mackay-Neal \
Progressive Edge-Growth
]
]
]
// #align(center + horizon)[
// #image("src/construction.jpg", width: 50%)
// ]
]
#myslide("Conclusion")[
#place(center + horizon, dy: 8.1cm)[#graphe_tanner_fond(0.9cm, 1.75)]
#place(center + horizon, dy: 7.7cm)[
#block(width: 100%)[
#text(size: 1.5em, weight: "bold", fill: black)[
QC-LDPC Encodage \
FPGA \
Test Réels
]
]
]
]
#[]<fin>
#myslide("Annexe")[
#v(6.5cm)
#align(center + horizon)[
#text(size: 100pt)[
Annexe
]
]
// #align(center + horizon)[
// #image("src/construction.jpg", width: 80%)
// ]
]
// #myslide("Définition : Matrice Génératrice")[
// #definition(titre: "Matrice Génératrice")[
// $G in cal(M)_(k,n)(FF_2)$ dont les lignes sont une base de $cal(C)$
// ]
//
// #definition(titre: "Encodage")[
// Pour un message $u in FF_2^k$ le mot de code $c in cal(C)$ est :
// $
// c = Phi(u) = u G
// $
// ]
//
// #definition(titre: "Forme systématique")[
// // TODO : changer [I_k | P] en un graphique jolie avec I et P dans un carré coloré
// $
// #dessiner_matrice($G =$, ((texte: $I_k$, largeur: 2.2, fond: gray.lighten(75%)), (texte: $P$, largeur: 2.2, fond: gray.lighten(75%))))
// // G = mat(
// // I_k, P;
// // augment: #1,
// // delim: "[",
// // )
// $
// ]
//
// #[
// #set text(size: 1.2em)
// // - Pour $u in FF_2^k, space display(u dot.o G = mat(u, u dot.o P; augment: #1, delim: "[",))$
//
// - Pour $u in FF_2^k, space #dessiner_matrice($u G =$, (
// (texte: $u$, largeur: 1.1, fond: gray.lighten(75%)),
// (texte: $u P$, largeur: 3.0, fond: gray.lighten(75%)),
// ))$
//
// - $P in cal(M)_(k ,n-k)(FF_2)$ matrice de parité\
// ]
// ]
//
// #myslide("Définition : Matrice de Contrôle")[
// #definition(titre: "Matrice de Contrôle")[
// // $H = mat(
// // P^top, I_(n-k);
// // augment: #1,
// // delim: "[",
// // )
// $
// #dessiner_matrice($H =$, (
// (texte: $P^top$, largeur: 2.2, fond: gray.lighten(75%)),
// (texte: $I_(n-k)$, largeur: 2.2, fond: gray.lighten(75%)),
// ))
// $
//
// ]
// #[
// #set text(size: 1.2em)
// - $cal(C) = ker(H) = {v in FF_2^n | H v^top = 0}$
//
// - $display(G H^top = 0)$
// ]
//
// #definition(titre: "Syndrome")[
// Pour un vecteur reçu $r = c + e, space s in FF_2^(n - k)$
// $
// s = H r^top = H c^top + H e^top = 0 + H e^top
// $
// ]
// #[
// #set text(size: 1.2em)
// - Si $s = 0, space r$ est un mot de code valide
// - Sinon $s$ donne la signature de l'erreur $e$
// ]
// // Possible décodage par syndrome
// ]
#myslide("Théorie derrière la définition des codes linaires")[
Poser les notations algebriques etc...
]
#myslide("Métriques : BER et FER")[
#set text(size: 18pt)
#definition(titre: "Bit Error Rate (BER)", accent: blue)[
$
"BER" = frac("Nombre de bits incorrects reçus", "Nombre total de bits transmis")
$
]
#v(0.6em)
#definition(titre: "Frame Error Rate (FER)", accent: orange)[
$
"FER" = frac("Nombre de trames avec au moins 1 bit incorrect", "Nombre total de trames")
$
]
#v(0.6em)
#grid(
columns: (1fr, 1fr),
gutter: 0.8em,
block(fill: blue.lighten(90%), stroke: 0.5pt + blue.lighten(30%), inset: 10pt, radius: 4pt)[
*Courbe Waterfall* : BER en fonction de $E_b \/ N_0$ (dB) — représente les performances
],
block(fill: orange.lighten(90%), stroke: 0.5pt + orange.lighten(30%), inset: 10pt, radius: 4pt)[
FER $>=$ BER \
Une seule erreur de bit invalide toute la trame
],
)
#v(0.5em)
// - *Monte-Carlo* : simuler un grand nombre de transmissions, collecter ~1000 erreurs par point
- $E_b\/N_0$ en dB $= 10 log_10 (E_b\/N_0)$
]
#myslide("Décodage par Maximum de Vraisemblance (ML)")[
#set text(size: 17pt)
#definition(titre: "Décodeur ML — Canal AWGN + BPSK")[
$
hat(bold(c)) = arg max_(bold(c) in cal(C)) P(bold(r) | bold(c)) = arg min_(bold(c) in cal(C)) d_H (bold(r), bold(c))
$
Minimiser la distance de Hamming au mot de code le plus proche.
]
#v(0.5em)
#definition(titre: "Décodage par Syndrome (Cosets)")[
- Calculer $bold(s) = bold(H) bold(r)^top$
- Chercher $bold(e)$ de poids minimal tel que $bold(H) bold(e)^top = bold(s)$
- Table préalculée de $2^(n-k)$ entrées $-->$ faisable seulement si $n - k$ petit
]
#v(0.5em)
#definition(titre: "Complexité — NP-Difficile (Berlekamp, 1978)", accent: red)[
Pour $bold(H)$ quelconque : $cal(O)(2^k)$ candidats à tester.
- $k = 648$ : $2^(648) approx 10^(195)$ — hors de portée
- Solution : exploiter la *structure creuse* de $bold(H)$ $-->$ BP itératif
]
]
#myslide("Codes LDPC Irréguliers")[
#set text(size: 17pt)
#definition(titre: "Distribution de Degrés (polynômes sur les arêtes)")[
$
lambda(x) = sum_i lambda_i x^(i-1), quad rho(x) = sum_j rho_j x^(j-1)
$
$lambda_i$ = fraction d'*arêtes* reliées à des VN de degré $i$, idem pour $rho_j$ et les CN.
]
#v(0.5em)
#grid(
columns: (1fr, 1fr),
gutter: 0.8em,
definition(titre: "Avantages", accent: green.darken(10%))[
- Approchent la limite de Shannon de plus près
- VN de degré 2 : accélèrent la convergence initiale du BP
],
definition(titre: "Optimisation", accent: orange)[
*Density Evolution* : calcule analytiquement le seuil de décodage en fonction de $(lambda, rho)$ $->$ optimiser numériquement
],
)
#v(0.5em)
- Contrainte : $n sum_i lambda_i \/ i = |E| = m sum_j rho_j \/ j$
- DVB-S2 ($n = 64800$) : degrés variables entre 2 et 13 selon le rendement cible
]
#myslide("Encodage Efficace : Richardson-Urbanke")[
#set text(size: 17pt)
*Problème :* Forme systématique $->$ $bold(G)$ dense $->$ $cal(O)(n^2)$ opérations.
#v(0.4em)
#definition(titre: "Forme ALT : Approximate Lower Triangular")[
Par permutation des lignes/colonnes de $bold(H)$ :
$
bold(H) = mat(A, B, T; C, D, E)
$
où $T$ est *triangulaire inférieure* de taille $(m-g) times (m-g)$, $g$ = gap (très petit).
]
#v(0.4em)
#definition(titre: [Encodage en $cal(O)(n + g^2)$])[
Résolution en 2 phases :
1. Calculer $bold(p)_1 in FF_2^g$ par élimination sur système de taille $g$
2. Calculer $bold(p)_2 in FF_2^(m-g)$ par back-substitution sur $T$ (triangulaire)
]
#v(0.3em)
- En pratique (QC-LDPC / 5G / DVB-S2) : encodage par *registres à décalage* $->$ $cal(O)(n)$
]
#myslide("Canal AWGN")[
#v(-0.8em)
#set text(size: 18pt)
#definition(titre: "Modèle du Canal — BPSK sur AWGN")[
$
y_i = x_i + n_i, quad x_i in {+1, -1}, quad n_i ~ cal(N)(0, sigma^2)
$
Mapping BPSK : bit $0 arrow.bar +1$, bit $1 arrow.bar -1$
]
#v(-0.5em)
#definition(titre: "Rapport Signal sur Bruit")[
$
frac(E_b, N_0) = frac(1, 2 R sigma^2)
$
$R = k\/n$ : rendement du code. Exprimé en dB : $10 log_10(E_b\/N_0)$.
]
#v(-0.5em)
#definition(titre: "LLR Initial sur Canal AWGN")[
$
L_"canal"(y_i) = ln frac(P(v_i = 0 | y_i), P(v_i = 1 | y_i)) = frac(2 y_i, sigma^2)
$
Le LLR est *proportionnel* à la valeur reçue $y_i$ : signe = décision, valeur absolue = confiance.
]
]
#myslide("Construction de Gallager (1962)")[
#set text(size: 17pt)
#definition(titre: "Principe")[
Empiler $w_c$ sous-matrices de taille $(m \/ w_c) times n$ :
$
bold(H) = mat(H_1; H_2; dots.v; H_(w_c))
$
]
#v(0.4em)
#set text(size: 21pt)
*Algorithme :*
- $H_1$ : blocs réguliers de $w_r$ uns consécutifs (colonnes disjointes)
- $H_2, dots, H_(w_c)$ : copies de $H_1$ avec colonnes *permutées aléatoirement*
#v(0.5em)
#set text(size: 17pt)
#grid(
columns: (1fr, 1fr),
gutter: 0.8em,
definition(titre: "Avantage", accent: green.darken(10%))[
Simple à construire.\
Garantit $w_c$ et $w_r$ exacts.
],
definition(titre: "Limite", accent: red)[
Cycles de longueur 4 fréquents.\
Aucun contrôle du girth.
],
)
]
#myslide("Construction de MacKay-Neal (1996)")[
#v(1.6cm)
#set text(size: 19pt)
#definition(titre: "Principe")[
Construction *aléatoire* de $bold(H)$ avec évitement actif des 4-cycles.
]
#v(0.4em)
#set text(size: 21pt)
*Algorithme* — pour chaque arête à placer $(v_j, c_i)$ :
1. Vérifier l'absence de 4-cycle : $exists.not (j', i')$ tel que $H_(i,j') = H_(i',j) = H_(i',j') = 1$
2. Si conflit : *rejeter* $c_i$ et tirer un autre nœud de contrôle
3. Sinon : ajouter l'arête
#v(0.5em)
#set text(size: 18pt)
#definition(titre: "Résultat")[
- Garanti *sans 4-cycles* par construction $->$ girth $>= 6$
// - Non garanti globalement (dépend de la densité)
// - Fondement théorique du BP (article MacKay & Neal, 1996)
- Performances nettement supérieures à Gallager
]
]
#myslide("Progressive Edge-Growth (Hu et al., 2005)")[
#set text(size: 18pt)
#definition(titre: "Idée")[
Construire les arêtes *une par une* en maximisant le *girth local* à chaque étape.
]
#v(0.4em)
#set text(size: 21pt)
*Algorithme* — pour relier $v_j$ à un nouveau CN :
1. BFS depuis $v_j$ dans le graphe courant
2. S'arrêter quand tous les CN ne sont plus accessibles à un nouveau niveau
3. Relier $v_j$ au CN de plus faible degré *non encore atteint* par le BFS
#v(0.5em)
#set text(size: 18pt)
#grid(
columns: (1fr, 1fr),
gutter: 0.8em,
definition(titre: "Avantage", accent: green.darken(10%))[
Maximise le girth global.\
Surpasse Gallager et MacKay.\
Standard de référence.
],
definition(titre: "Complexité", accent: orange)[
$cal(O)(n dot w_c dot m)$\
Coût en construction seulement, pas en décodage.
],
)
]
#myslide("Limite de Shannon : Canal AWGN")[
#set text(size: 18pt)
#definition(titre: "Capacité du Canal AWGN (Shannon, 1948)")[
$
C = 1/2 log_2(1 + "SNR") quad ["bit / utilisation"]
$
Il *existe* un code de rendement $R < C$ avec BER $-> 0$ quand $n -> infinity$.
]
#v(0.6em)
- Pour $R = 1/2$ : limite à $E_b\/N_0 approx 0.19$ dB
- *Bit-Flipping* : ~56 dB de la limite
- *Sum-Product* : ~11.5 dB de la limite
#v(0.5em)
#definition(titre: "Pourquoi de grands blocs ?", accent: orange)[
Loi des grands nombres : pour $n$ grand, le bruit moyen par bit converge vers $sigma^2$.
Plus $n$ est grand, plus on s'approche de la limite — au prix de la latence.
]
#v(0.3em)
#align(center)[
#block(fill: gray.lighten(90%), stroke: 0.5pt + gray, inset: 10pt, radius: 4pt)[
DVB-S2 : $n = 64800$ → à 0.5 dB de Shannon pour $R = 1/2$.
]
]
]
#myslide("Girth : Impact sur la Convergence du BP")[
#v(0.7cm)
#set text(size: 17pt)
#definition(titre: [BP Exact $->$ Graphe = Arbre])[
Sans cycle, le BP est *exact* et converge en au plus diamètre(graphe) itérations.
]
#v(0.4em)
#definition(titre: "Problème des Cycles Courts", accent: red)[
Un cycle de longueur $2l$ : après $l$ itérations, un message revient à son point de départ.
$->$ *Violation de l'indépendance* des messages $->$ BP sous-optimal.
]
#v(0.4em)
- *Girth = 4* : deux VN partagent deux CN $->$ corrélation immédiate, convergence vers solution incorrecte
- *Girth = 6* : premier retour après 3 itérations $->$ stable pour quelques itérations
// - PEG garantit girth $>= 6$ voire $8$ pour $n = 1296$, $w_c = 3$
#v(0.4em)
#align(center)[
#block(fill: gray.lighten(90%), stroke: 0.5pt + gray, inset: 10pt, radius: 4pt)[
girth $>= 2 I_"max"$ pour que les cycles n'affectent pas les $I_"max"$ premières itérations.
]
]
]
#myslide("QC-LDPC : Codes Quasi-Cycliques")[
#v(1cm)
#set text(size: 18pt)
#definition(titre: [Structure de $bold(H)$ — Blocs Circulants])[
$
bold(H) = mat(
bold(Pi)^(p_(0,0)), dots, bold(Pi)^(p_(0,n_b-1));
dots.v, dots.down, dots.v;
bold(Pi)^(p_(m_b-1,0)), dots, bold(Pi)^(p_(m_b-1,n_b-1))
)
$
$bold(Pi)^p$ = identité décalée de $p$ lignes ($p = -1$ $->$ matrice nulle)
]
#v(0.5em)
#definition(titre: "Avantages")[
- *Stockage* : seulement la matrice des exposants $p_(i,j)$ (taille $m_b times n_b$)
- *Encodage* : registres à décalage $->$ $cal(O)(n)$ simple en FPGA
- *Décodage* : $Z$ noeuds traités en parallèle par bloc circulant
]
#v(0.3em)
- DVB-S2 : $Z = 360$, $m_b times n_b = 45 times 90$ $->$ $n = 64800$
]
#myslide("Convergence du BP : Comportement et Critères")[
#set text(size: 17pt)
#definition(titre: "Hypothèse du BP")[
Messages entrants en un nœud supposés *indépendants*.
$->$ Exact sur arbre, *approché* sur graphe à grand girth.
]
#v(0.4em)
#definition(titre: "Critères d'Arrêt")[
- *Syndrome nul* : $bold(H) hat(bold(c))^top = bold(0)$ $->$ succès, on s'arrête
- *$I_"max"$ itérations* atteint $->$ échec $->$ paquet perdu (compte dans le FER)
]
#v(0.4em)
- En pratique : $I_"max" = 50$$200$ selon le code et le SNR
- Oscillations possibles autour d'un *pseudo-mot de code* (trapping set)
- Plus le SNR est élevé, moins d'itérations nécessaires
#v(0.4em)
#align(center)[
#block(fill: gray.lighten(90%), stroke: 0.5pt + gray, inset: 10pt, radius: 4pt)[
Analyse théorique : *Density Evolution* — calcule le seuil exact de convergence en fonction de $E_b\/N_0$.
]
]
]
#myslide("CN Update : Formalisme probabiliste")[
#set text(size: 16pt)
#v(-0.3em)
Soit $(V_u)_(u in cal(N)(c))$ une famille de variables aléatoires mutuellement indépendantes à valeurs dans $bb(F)_2$.
On cherche à déterminer la loi du message $R_(c arrow v)(b)$ envoyé par le nœud de contrôle $c$.
#v(0.5em)
#definition(titre: "Conditionnement de l'événement de parité", accent: orange)[
Le message $R_(c arrow v)(b)$ correspond à la probabilité conditionnelle :
#v(0.5em)
#align(center)[#scale(105%)[$
display(R_(c arrow v)(b) = P( limits(xor.big)_(u in cal(N)(c)) V_u = 0 | V_v = b ))
$]]
Par linéarité du XOR, cette condition est équivalente à :
#v(0.5em)
#align(center)[#scale(110%)[$ display(P( limits(xor.big)_(u in cal(N)(c) backslash {v}) V_u = b )) $]]
]
Par le théorème des probabilités totales appliqué au système complet d'événements associé aux configurations $x in bb(F)_2^(d_c - 1)$ des voisins :
#align(center)[#scale(120%)[
$
display(R_(c arrow v)(b) = limits(sum)_(x in bb(F)_2^(d_c - 1) \ limits(xor.big)_(u in cal(N)(c) backslash {v}) x_u = b) limits(product)_(u in cal(N)(c) backslash {v}) P(V_u = x_u))
$
]]
]
#myslide("CN Update (1) : Probabilités")[
#set text(size: 16pt)
#v(-0.3em)
En utilisant les messages entrants du graphe, on définit la probabilité locale $P_(u arrow c)(x_u) = P(V_u = x_u)$.
Le message sortant devient :
#v(1em)
#align(center)[#scale(130%)[
$
display(R_(c arrow v)(b) = limits(sum)_(x in bb(F)_2^(d_c - 1) \ limits(xor.big)_(u in cal(N)(c) backslash {v}) x_u = b) quad limits(product)_(u in cal(N)(c) backslash {v}) P_(u arrow c)(x_u))
$
]]
#v(1em)
Exacte mais complexité est exponentielle en $d_c$. On utilise alors une transformation pour simplifier le calcul (Lemme de Gallager).
]
#myslide("CN Update (2) : Lemme de Gallager")[
#set text(size: 16pt)
#block(
fill: orange.lighten(94%),
stroke: (left: 4pt + orange),
radius: 4pt,
inset: 20pt,
width: 100%,
)[
*Lemme de Gallager* -- Soient $(X_1, ..., X_n)$ des variables de Bernoulli indépendantes sur $bb(F)_2$.
#v(0.5em)
#align(center)[#scale(130%)[
$ display(P(limits(xor.big)_(i=1)^n X_i = 0) = 1/2 (1 + limits(product)_(i=1)^n E[(-1)^(X_i)])) $
]]
]
#v(0.8em)
// L'espérance correspond au biais de la loi, noté $delta_(u arrow c)$ :
On notes :
#align(center)[#scale(120%)[
$ display(E[(-1)^(V_u)] = delta_(u arrow c) = P(V_u = 0) - P(V_u = 1) = 1 - 2 P_(u arrow c)(1)) $
]]
#v(0.8em)
On en déduit les probabilités marginales conditionnelles pour le nœud $c$ :
#align(center)[#scale(120%)[
$
display(R_(c arrow v)(0) = 1/2 (1 + limits(product)_(u in cal(N)(c) backslash {v}) delta_(u arrow c)))
space , space space
display(R_(c arrow v)(1) = 1/2 (1 - limits(product)_(u in cal(N)(c) backslash {v}) delta_(u arrow c)))
$
]]
]
#myslide("CN Update (3) : Passage aux LLR")[
#set text(size: 16pt)
#definition(titre: "Log-Likelihood Ratio (LLR)", accent: blue.darken(10%))[
Le message LLR entrant $m_(u arrow c)$ au nœud de contrôle est défini par :
#v(0.5em)
#align(center)[#scale(130%)[
$ display(m_(u arrow c) = ln(frac(P(V_u = 0), P(V_u = 1)))) $
]]
]
#v(0.8em)
$delta_(u arrow c)$ s'exprime alors :
#align(center)[#scale(130%)[
$ display(delta_(u arrow c) = tanh(frac(m_(u arrow c), 2))) $
]]
#v(0.8em)
On en déduit le LLR du message sortant :
#align(center)[#scale(130%)[
$
display(m_(c arrow v) = ln(frac(R_(c arrow v)(0), R_(c arrow v)(1))) = 2 tanh^(-1) ( limits(product)_(u in cal(N)(c) backslash {v}) tanh(frac(m_(u arrow c), 2)) ))
$
]]
]
#myslide("CN Update (4) : Algorithmes")[
#set text(size: 16pt)
#block(
fill: orange.lighten(92%),
stroke: (left: 4pt + orange),
radius: 4pt,
inset: 15pt,
width: 100%,
)[
*Sum-Product* -- Mise à jour :
#v(0.5em)
#align(center)[#scale(130%)[
$
display(m_(c arrow v) = 2 tanh^(-1) ( limits(product)_(u in cal(N)(c) backslash {v}) tanh(frac(m_(u arrow c), 2)) ))
$
]]
]
#v(1em)
#block(
fill: blue.lighten(92%),
stroke: (left: 4pt + blue),
radius: 4pt,
inset: 15pt,
width: 100%,
)[
*Min-Sum* -- Approximation :
#v(0.5em)
#align(center)[#scale(130%)[
$
display(m_(c arrow v) approx ( limits(product)_(u in cal(N)(c) backslash {v}) op("sgn")(m_(u arrow c)) ) times limits(min)_(u in cal(N)(c) backslash {v}) |m_(u arrow c)|)
$
]]
]
]
#myslide("Bit-Flipping : Choix du Seuil")[
#set text(size: 18pt)
- Chaque $v_j$ reçoit de ses $w_c$ voisins $c_i$ un verdict $f_i in {0, 1}$.
- Il retourne son bit si *trop d'équations échouent*.
#v(0.3em)
#definition(titre: "Stratégies de Seuil")[
#table(
columns: (1.2fr, 1.4fr, 1.4fr),
inset: 8pt,
stroke: 0.5pt + gray,
align: center + horizon,
[*Seuil*], [*Règle*], [*Remarque*],
[Majorité stricte], [$k_j > w_c \/ 2$], [Standard Gallager-A],
[Seuil fixe $b$], [$k_j >= b$ (ex. $b = w_c$)], [Gallager-B : plus conservateur],
[Tous insatisfaits], [$k_j = w_c$], [Très conservateur, peu de faux retournements],
)
]
#v(0.5em)
- *Gallager-A* : retourner si *toutes* les contraintes sont violées ($k_j = w_c$) — évite les oscillations
- *Gallager-B* : retourner si *au moins* $b < w_c$ contraintes violées — plus agressif
]
#myslide("Que Se Passe-t-il en Cas d'Échec ?")[
#set text(size: 16pt)
#definition(titre: "Échec du Décodeur")[
Après $I_"max"$ itérations : syndrome $bold(s) != bold(0)$ $->$ *décodage échoué*. \
Deux scénarios :
- La trame est *perdue* (FPV, diffusion) $->$ acceptable
- La trame doit *arriver* (fichier, protocole) $->$ retransmission nécessaire
]
#v(0.5em)
#definition(titre: "ARQ — Automatic Repeat reQuest")[
Le récepteur envoie un *NACK* (Not Acknowledged) : l'émetteur retransmet.
- *Stop-and-Wait* : simple mais inefficace
- *Hybrid ARQ (HARQ)* : combine ARQ + codage — standard LTE/5G
]
#v(0.4em)
#definition(titre: "HARQ Type II — Chase Combining", accent: blue)[
Le récepteur *combine* les LLR des deux transmissions avant de redécoder :
$
L_"total"(y_i) = L_"canal"^1(y_i) + L_"canal"^2(y_i)
$
// $->$ gain de $3$ dB sans retransmission supplémentaire effective.
]
]
#myslide("Limites du Modèle AWGN")[
#v(-0.7cm)
#set text(size: 17pt)
#definition(titre: "Ce que AWGN suppose")[
- Bruit *blanc* : indépendant d'un symbole à l'autre
- Distribution *gaussienne* stationnaire
- Canal *sans mémoire* : chaque bit perturbé indépendamment
]
#v(-0.2cm)
#definition(titre: "Canaux Réels — Ce qu'AWGN ne capture pas", accent: red)[
#table(
columns: (1.2fr, 1.8fr),
inset: 8pt,
stroke: 0.5pt + gray,
[*Phénomène*], [*Impact*],
[Évanouissements (fading)], [SNR varie dans le temps],
[Burst d'erreurs], [Bits consécutifs corrompus $->$ entrelacement nécessaire],
[Bruit impulsionnel], [Pics de bruit ponctuels (moteurs, orages)],
[Erreurs de phase], [Synchronisation imparfaite en BPSK],
)
]
#v(-0.2cm)
#definition(titre: "Solution : Entrelacement", accent: blue)[
Permuter les bits *avant* l'envoi : les erreurs en rafale deviennent des erreurs isolées pour le décodeur LDPC.
]
]
#set raw(theme: "ldpc-theme.tmTheme")
#generer_slides_code(
(
"src/rs/code.rs",
"src/rs/encoder.rs",
"src/rs/decoder.rs",
"src/rs/generator.rs",
"src/rs/channel.rs",
"src/rs/matrix.rs",
"src/rs/lib.rs",
),
myslide,
graphe_tanner_fond,
)