Implémentation et étude d’un turbo-code Sujet proposé par Jean-Pierre Tillich Distribué le 12 novembre 2010 1 Quelques mots sur le sujet Le but de ce projet est de réaliser et de comprendre comment fonctionne un code correcteur d’erreurs très proche des codes correcteurs qui sont standardisés actuellement, c’est à dire un turbo-code. Comme cela sera vu dans le cours 8, les codes convolutifs agissent comme d’excellents codes réducteurs de bruit sur le canal, mais souffrent du fait que si l’on veut réaliser le décodage au maximum de vraisemblance avec l’algorithme de Viterbi, la mémoire du code doit rester petite : typiquement des mémoires de l’ordre de 3 − 7 sont usuelles, et de l’ordre de 10 − 15 très exceptionnellement utilisées. Le fait que la probabilité d’erreur par bit après décodage reste élevée, typiquement de l’ordre de 10−3 − 10−4 font que l’on est obligé d’utiliser d’autres moyens de codage. Jusqu’au début des années 1990, la solution la plus répandue consistait à concaténer un code convolutif avec un code de Reed-Solomon (voir cours 7). Berrou et Glavieux ont proposé en 1993 une solution à la fois de faible complexité et beaucoup plus performante en termes de probabilité d’erreur après décodage : les turbo-codes. Cette solution consiste à combiner de manière astucieuse deux codes convolutifs de manière à obtenir des codes : (i) dont la distance minimale n’est plus bornée par une constante quand la longueur augmente, (ii) qui peuvent être décodés en décodant les deux codes convolutifs de base, et en combinant de manière adéquate le résultat des deux décodages au moyen d’une procédure itérative. Avec un choix convenable des codes convolutifs, les performances peuvent être excellentes. Dans ce projet, il s’agira essentiellement d’effectuer la programmation d’un turbo-code et de répondre à un certain nombre de questions d’approfondissement (ou de compréhension) données ici. 2 Une description d’un certain type de code convolutif Par souci de simplification, nous allons nous intéresser qu’à des turbo-codes construits en utilisant des codeurs convolutifs de type (2, 1) (il est à signaler que cela concerne bon nombre de turbo-codes utilisés en pratique, comme par exemple les turbo-codes mis en oeuvre dans la 3G). Définition 1 (Codeur convolutif de type (2, 1) de mémoire m) 1. Un codeur convolutif de type (2, 1) de mémoire m est caractérisé par la donnée de trois mots binaires de longueur m + 1, a1 = (a10 , . . . , a1m ), a2 = (a20 , . . . , a2m ) et b = (b0 , . . . , bm ). 2. Il associe à une séquence d’information d’une taille arbitraire K : u = (u1 , . . . , uK ) ∈ {0, 1}K deux séquences x1 = (x11 , . . . , x1K ) ∈ {0, 1}K et x2 = (x21 , . . . , x2K ) ∈ {0, 1}K . 3. Celles-ci sont obtenues en calculant K vecteurs d’“état” auxiliaires s1 = (s1,1 , . . . , s1,m ), . . . , sK = (sK,1 , . . . , sK,m ) appartenant à {0, 1}m via les formules (on effectue les sommes et les produits dans 1 le corps à deux éléments) s0 = |0 .{z . . 0} m si = (bm ui + m−1 X bj si−1,m−j , si−1,1 , . . . , si−1,m−1 ) pour i ≥ 1. j=0 En d’autres termes si est obtenu au moyen de si−1 et de ui en décalant les coordonnées de si−1 vers la droite, et en prenant pour première coordonnée une certaine combinaison linéaire fixe des coordonnées de si−1 et de ui . 4. Les séquences x1 et x2 sont alors obtenues par les formules x1i = a1m ui + m−1 X a1j si−1,m−j j=0 x2i = a2m ui + m−1 X a2j si−1,m−j j=0 Il sera commode d’introduire la fonction e : {0, 1} × {0, 1}m → {0, 1}m (u, s1 , . . . , sm ) 7→ (bm u + m−1 X bj sm−j , s1 , . . . , sm−1 ) j=0 En d’autres termes si = e(ui , si−1 ). Un tel codeur peut être réalisé très simplement en prenant un registre à décalage de taille m stockant les coordonnées de si au temps i. Dans le cas des turbo-codes nous serons plus particulièrement intéressé par des codeurs systématiques et récursifs. Ils sont définis par : Définition 2 (Codeur convolutif de type (2, 1) systématique) Un tel codeur correspond au choix a1 = (0, . . . , 0, 1), c’est à dire a1i = 0 pour i ∈ {0, . . . , m − 1} et a1m = 1. En d’autres termes, la séquence d’entrée u se retrouve copiée intégralement dans la séquence de sortie x1 : x1i = ui pour tout i strictement positif. On appelle dans ce cas la suite x2 la redondance du codeur. Par la suite on fera toujours l’hypothèse que bm = 1. Pour expliciter l’algorithme de décodage des turbo-codes, il sera intéressant de considérer les fonctions suivantes définies pour un codeur convolutif systématique de type (2, 1) de mémoire m : u : {0, 1}m × {0, 1}m m m r : {0, 1} × {0, 1} → {0, 1, ∞} → {0, 1, ∞} u(s, s0 ) est défini comme égal à l’infini si et seulement il n’existe pas de valeur de x ∈ {0, 1} telle que e(x, s) = s0 et est égale à l’unique valeur de x telle que e(x, s) = s0 sinon. r(s, s0 ) est infini si et seulement si u(s, s0 ) est infini. Dans le cas contraire on a r(s, s0 ) = a2m u(s, s0 ) + m−1 X a2j sm−j j=0 (avec s = (s1 , . . . , sm )). Définition 3 (Codeur convolutif de type (2, 1) récursif ) Un codeur de type (2, 1) est dit récursif si au moins un des bi pour i dans {0, . . . , m − 1} est non nul. 2 Question 1 Considérer une séquence d’information u de taille arbitraire ne comportant qu’un seul 1. Que peut-on dire des poids des sorties x1 et x2 dans le cas d’un codeur non récursif et dans le cas d’un code récursif ? 3 Un turbo-code Voyons maintenant plus précisément ce qu’est un turbo-code. Dans la littérature existent de nombreuses variantes du schéma de base de Berrou et Glavieux. Ce que nous appelons turbo-code dans cette section porte généralement dans la littérature le nom de turbo-code parallèle. Cela correspond au schéma de codage suivant. Définition 4 Le turbo-code de longueur d’information k associé à deux codages convolutifs systématiques u → (u, r1 (u)) et u → (u, r2 (u)) 1 et à une permutation π de {1, 2, . . . , k} est l’ensemble des mots de la forme (u, r1 (u), r2 (uπ ), où u = (ui )1≤i≤l désigne un mot binaire de longueur k et uπ désigne le vecteur (uπ(i) )1≤i≤k . En d’autres termes le codage consiste à prendre la sortie d’un premier code convolutif (que l’on suppose systématique), puis à faire rentrer la même séquence d’entrée permutée par π dans un deuxième codeur convolutif systématique, mais à ne rajouter que la sortie du deuxième code correspondant à la redondance. Question 2 Pouvez-vous donner une borne sur la distance minimale d’un turbo-code utilisant deux codeurs de type (2, 1) non récursifs de mémoire m ? 4 Décodage du turbo-code Si l’on sait décoder un code convolutif au maximum de vraisemblance avec une complexité réduite, pour peu que la mémoire du codeur convolutif soit petite, il n’en va pas de même d’un turbo-code : en effet, on ne sait pas décoder de tels codes au maximum de vraisemblance avec une complexité raisonnable, et ce même si la mémoire des deux codes convolutifs constituants est petite. Cependant, on peut les décoder de manière sous-optimale avec un algorithme de décodage itératif. Une itération consiste principalement à décoder avec l’algorithme de Viterbi chaque code convolutif séparément, puis à donner le résultat de ce décodage à l’autre code convolutif pour l’itération suivante. Nous allons maintenant donner cet algorithme de décodage pour le canal binaire symétrique. En substance, chaque décodage de type Viterbi consiste à appliquer un algorithme dont les entrées et sorties correspondent à la description donnée dans l’algorithme 3. L’algorithme itératif consiste ici alors à effectuer l’algorithme 2 Avec la description d’un code convolutif vue précédemment, la description précise de l’algorithme SOVA devient Question 3 A la première itération, voyez-vous un lien entre les quantités f [i][b] et certaines quantités calculées par l’algorithme de Viterbi ? Question 4 Implémenter cet algorithme pour plusieurs choix de codes convolutifs de type (2, 1), plusieurs valeurs de permutation et des valeurs de k comprises entre 500 et 10000. Evaluer les performances pour des valeurs de probabilité d’erreur sur le canal binaire symétrique variant entre p = 0.05 et p = 0.15. Voyez-vous une différence entre utiliser des codeurs récursifs et non récursifs ? Pouvez-vous donner une explication de ce phénomène ? 1. Ici ri (u) désigne la redondance correspondant à l’entrée u pour le i-ème codeur convolutif. 3 Algorithme 1 Squelette du décodage de type Viterbi (ou SOVA dans la littérature pour Soft Output Viterbi Algorithm) SOVA(u, e, r) INPUT : 1. u la séquence d’information reçue par le canal, 2. e = (e[i][b])1≤i≤k une information de fiabilité pour chaque bit d’information, pour chaque bit 0≤b≤1 i on dispose de deux quantités, e[i][0] et e[i][1], si la deuxième quantité est plus grande que la première c’est une indication qu’il est plus vraisemblable que ui soit égal à 0, sinon c’est la valeur de 1 qui est plus vraisemblable. 3. r la redondance telle qu’elle a été reçue par le canal OUTPUT : e0 une nouvelle information de fiabilité pour chaque bit d’information. Algorithme 2 Algorithme de décodage itératif du turbo-code INPUT: - u = (u1 , . . . , uk ) la séquence d’information reçue, - r1 la redondance reçue pour le premier code convolutif, - r2 le redondance reçue pour le second code convolutif. - π une permutation de {1, . . . , k}. OUTPUT: û = (û1 , . . . , ûk ) la séquence d’information décodée {Initialisation (on met à zéro toutes les entrées de e1 et e2 )} e1 ← 0 e2 ← 0 {Itérations. Le nombre d’itérations N est compris entre 5 et 20 environ en pratique.} for t = 1 to N do e2 ← SOVA(u, e1π−1 , r1 ) e1 ← SOVA(uπ , e2π , r2 ) {Etape finale (on note δa=b la fonction de Kronecker)} for i = 1 to k do if δui =1 + e1 [π −1 (i)][0] + e2 [i][0] ≤ δui =0 + e1 [π −1 (i)][1] + e2 [i][1] then ûi ← 0 else ûi ← 1 4 Algorithme 3 Squelette du décodage de type Viterbi (ou SOVA dans la littérature pour Soft Output Viterbi Algorithm) SOVA(u, e, r) INPUT : 1. u = (u1 , . . . , uk ) la séquence d’information reçue par le canal, 2. e = (e[i][b])1≤i≤k une information de fiabilité pour chaque bit d’information, pour chaque bit 0≤b≤1 i on dispose de deux quantités, e[i][0] et e[i][1], si la deuxième quantité est plus grande que la première c’est une indication qu’il est plus vraisemblable que ui soit égal à 0, sinon c’est la valeur de 1 qui est plus vraisemblable. 3. r = (r1 , . . . , rk ) la redondance reçue par le canal OUTPUT : e0 une nouvelle information de fiabilité pour chaque bit d’information. {Initialisation de deux tableaux bidimensionnels, m est la mémoire du codeur convolutif.} for i = 0 to k − 1 do for a = 0 to 2m − 1 do f [i][a] ← ∞ b[i][a] ← ∞ f [0][0] ← 0 for a = 0 to 2m − 1 do b[k][a] ← 0 {Calcul des valeurs finales des tableaux b et f (noter que l’on voit ici un mot binaire de longueur m comme un nombre entier dans {0, 1, . . . , 2m − 1})} for i = 1 to k − 1 do for b = 0 to 2m − 1 do f [i][b] ← min0≤a≤2m −1:u(a,b)6=∞ f [i − 1][a] + e[i][u(a, b)] + δui =1−u(a,b) + δri =1−r(a,b) b[k − i][b] ← min0≤a≤2m −1:u(b,a)6=∞ b[k − i + 1][a] + e[k − i + 1][u(b, a)] + δuk−i+1 =1−u(b,a) + δrk−i+1 =1−r(b,a) {Calcul du tableau de fiabilité e0 } for i = 1 to k do e0 [i][0] = min0≤a,b≤2m −1:u(a,b)=0 f [i − 1][a] + b[i][b] + δri =1−r(a,b) e0 [i][1] = min0≤a,b≤2m −1:u(a,b)=1 f [i − 1][a] + b[i][b] + δri =1−r(a,b) return e0 5 Question 5 Proposer une (très) légère modification de l’algorithme de manière à pouvoir corriger les effacements sur un canal à effacement. Recommencer les simulations précédentes pour des probabilités d’effacement de l’ordre de p = 0.5. 5 Extensions possibles Suivant l’état d’avancement du projet, plusieurs extensions (facultatives) du projet seront proposées ici. 6