Correção de Erros I: Simulador de Códigos Reed-Solomon Embora sejam famosos por sua eficácia na correção de erros de símbolos, com aplicações em diversas áreas da engenharia, os códigos de Reed-Solomon são pouco conhecidos em profundidade, o que dificulta a sua implementação prática. Visando a esclarecer sua lógica e os mecanismos internos de codificação e decodificação, esta série de tutoriais tem como objetivo apresentar o desenvolvimento de um simulador genérico para códigos de Reed-Solomon. Os tutoriais foram preparados a partir do trabalho de conclusão de curso “Desenvolvimento de um Simulador de Codificação-Decodificação Reed-Solomon”, elaborado pelos autores, e apresentado à Coordenação de Pós-graduação Lato Sensu do Inatel como requisito parcial para a obtenção do Certificado de Conclusão do Curso de Engenharia de Sistemas de TV Digital e IPTV. Foi orientador do trabalho o Prof. Dr. Geraldo Gil R. Gomes. Este tutorial parte I apresenta os fundamentos teóricos dos códigos Reed-Solomon, detalhando suas características, aspectos teóricos dos campos finitos e a codificação e decodificação desses códigos. Daniel Cursi Junior Engenheiro Eletroeletrônico pela Fundação Educacional de Barretos (SP, 1987), também é graduado em Administração de Empresas com ênfase mercadológica pelo IMESB-VC de Bebedouro (SP, 1998), pós-graduado em Administração de Empresas (MBA) pela Fundace/USP de Ribeirão Preto e pós-graduado em Televisão Digital pelo Inatel de Santa Rita do Sapucai (MG). Desde 1993 é sócio de uma empresa do Setor de Telecomunicações, prestando serviços de consultoria e manutenção de sistemas de retransmissão de televisão aberta. Email: [email protected] 1 Júlio Tanomaru Engenheiro Eletrônico pelo Instituto Tecnológico de Aeronáutica (ITA, 1986), completou o mestrado como bolsista do governo japonês na Universidade de Tokushima (Japão, 1992). Com pesquisas centradas em aplicações de Inteligência Computacional em Engenharia, obteve o título de Doutor em Engenharia na mesma universidade em 1996. Atuou como Professor Auxiliar na Universidade de Tokushima (Japão). Após deixar a universidade, atuou como Gerente de Projetos de software na Fujitsu Tokushima Systems Engineering. Atualmente é Professor no Centro Universitário Unilins, onde leciona diversas disciplinas relacionadas à Engenharia de Computação e Eletrônica. Email: [email protected] Categorias: Telefonia Celular, TV e Rádio Nível: Introdutório Enfoque: Técnico Duração: 15 minutos Publicado em: 22/08/2010 2 Correção de Erros I: Introdução Motivação Isoladamente ou em conjunto com outras formas de codificação, os códigos de Reed-Solomon estão certamente entre os códigos corretores de erros com maior número de aplicações práticas. Embora muito conhecidos pelo nome e por suas principais características, poucos são os que compreendem seu funcionamento interno a ponto de poder implementá-los desde o início. Isso se deve a vários fatores, incluindo: o entendimento de sua lógica demanda conhecimentos de álgebra de conjuntos finitos; há falta de material de referência que explique como implementar decodificadores práticos; a maior parte dos livros mostra somente exemplos simples, que podem ser resolvidos à mão com uso de álgebra, e não entra em detalhes sobre como fazer implementações práticas em software ou hardware. O desejo de entender a fundo o funcionamento dos códigos de Reed-Solomon constitui a principal motivação deste trabalho. Além de permitir sua real utilização prática, esse entendimento profundo possibilita a otimização e melhoria dos códigos, comparação de algoritmos de decodificação, e até mesmo a implementação em hardware utilizando tecnologias de hardware reconfigurável como os FPGA (Field Programmable Gate Arrays). Objetivo Este trabalho tem o objetivo prático de implementar um simulador em software de códigos de Reed-Solomon, facilitando o entendimento dessa importante categoria de códigos. Para isso, os procedimentos de codificação e decodificação são estudados em detalhe e é desenvolvida uma biblioteca de classes na linguagem de programação Java. O simulador consiste numa aplicação Java com interface gráfica rica e intuitiva, permitindo simulações de códigos Reed-Solomon com grande variedade de parâmetros. A biblioteca de classes é totalmente original, desenvolvida a partir do zero, e é disponibilizada gratuitamente a interessados. As classes e seus métodos foram escritos de modo a facilitar sua compreensão e utilização, sem grande ênfase à eficiência de execução. Tutoriais Este tutorial parte I apresenta os fundamentos teóricos dos códigos Reed-Solomon, detalhando suas características, aspectos teóricos dos campos finitos e a codificação e decodificação desses códigos. O tutorial parte II apresentará inicialmente a biblioteca desenvolvida em linguagem de programação Java contendo as classes para realização de álgebra de Campos de Galois, e a seguir apresentará o simulador propriamente dito e as conclusões do trabalho realizado. 3 Correção de Erros I: Fundamentos Fundamentos dos Códigos Reed-Solomon Num trabalho publicado em 1960, Irving Reed e Gus Solomon [9] propuseram uma classe de códigos corretores de erros que hoje encontram aplicações das mais variadas, tais como tocadores de CDs e codificadores de televisão digital. Esses códigos, largamente conhecidos como Códigos de Reed- Solomon (códigos R-S), são códigos cíclicos e não binários, onde cada símbolo é formado por uma sequência de m bits, onde m > 2. Os códigos R-S são normalmente denotados como R-S (n, k), onde n indica o número total de símbolos num bloco codificado e k é o número de símbolos de dados (mensagem original) do bloco. Dado o número de bits por símbolo, m, existem códigos R-S para todos os valores n e k que obedeçam à relação: 0 < k < n < 2m + 2 (1) Nos códigos R-S mais comuns: (n, k) = (2m – 1,2m – 1 – 2t) (2) Onde t dá a capacidade de correção de erros de símbolo do código. Nota: O padrão brasileiro de televisão digital emprega símbolos de m = 8 bits, n = 204 e k = 188 e, portanto, tem capacidade para correção de t = 8 símbolos. Características Apresentam-se a seguir as características dos código Reed-Solomon. Distância Mínima Para um dado número de símbolos de entrada e saída do decodificador, os códigos R-S resultam na maior distância mínima possível para qualquer código linear [10]. Esses códigos são capazes de corrigir t ou menos erros de símbolo, onde: t = [(n – k) / 2] (3) O operador [x] indica o maior inteiro que não excede x, ou seja, o número de símbolos que o decodificador é capaz de corrigir é metade do número de símbolos de paridade que o codificador insere. Conforme já exposto, os códigos R-S são não-binários e utilizam símbolos de m bits cada. Num código binário (n, k), de um total de 2n n-tuples possíveis, 2k n-tuples são palavras-código válidas. Essa fração é reduzida significativamente no caso de códigos R-S, o que possibilita maiores distâncias mínimas. Por exemplo, considerando (n, k) = (255, 235), no caso binário a fração das n-tuplas que são palavras-código válidas é de 2-20; no caso não-binário, considerando m = 8 bits por símbolo, essa fração cai para 2km / 2nm = 21880 / 22040 = 2-160. 4 Robustez a Erros em Rajada Os códigos R-S são particularmente eficientes para correção de erros em rajada. Consideremos um ruído em rajada de duração de l bits afetando a transmissão de um bloco de um código R-S (n, k) com m bits por símbolo. Se l = 1, é óbvio que somente um símbolo será afetado; se l = 2, então 1 ou 2 símbolos serão afetados pelo ruído, dependendo da localização da rajada em relação aos bits de início ou fim de um símbolo. Essa situação é ilustrada com um exemplo na figura 1, onde ns indica o número de símbolos afetados pelo ruído em rajada. Estendendo esse raciocínio, é possível determinar o menor comprimento de rajada em bits necessário para afetar um dado número de símbolos. Por exemplo, o menor comprimento de rajada para o qual 3 símbolos poderão ser afetados é l = m + 2 (neste caso, a rajada afeta um símbolo por inteiro e um bit em cada símbolo vizinho). Para rajadas de comprimento até 4m bits, o número de símbolos afetados é mostrado na tabela 1. Por exemplo, para o exemplo da figura 1, l = 14 = 3m+2 e, portanto, da tabela segue que o número de símbolos afetados, ns, fica entre 4 e 5. Figura 1: Efeito de uma rajada de l = 14 bits num fluxo de símbolos com m = 4 bits cada. O número de símbolos corrompidos, ns, será de 4 ou, no pior caso, 5. Tabela 1: Símbolos afetados por erros em rajada Comprimento da rajada Número de símbolos afetados 1 1 2...m 1...2 m+1 2 (m + 2)...2m 2...3 2m + 1 3 2m + 2...3m 3...4 3m + 1 4 3m + 2...4m 4...5 A partir da tabela 1, pode-se concluir que o menor e o maior comprimento de rajada capaz de afetar ns >= 2 símbolos de m bits podem ser determinados, respectivamente, por: lmin = (ns – 2) m + 2 (4) lmax = (ns – 1) m + 1 (5) Um código R-S (n, k) é capaz de corrigir até t = (n–k) / 2 símbolos errados, independentemente das 5 localizações desses símbolos dentro da palavra-código. Se t símbolos incorretos aparecerem em sequência, como resultado de uma rajada, teremos que garantidamente o código suporta rajadas de até lmax bits, ou seja: lmax = (((n–k) / 2) – 1) m + 1 = (t – 1) m + 1 (6) Por exemplo, considerando o código R-S (204, 188) utilizado no Sistema Brasileiro de Televisão Digital, com m = 8 bits por símbolo, teremos uma imunidade a rajadas de até (8–1) x 8+1 = 57 bits, o que é notavelmente superior ao desempenho de códigos binários. Obviamente, no caso de um canal sem memória, em que os erros não ocorram em rajadas, mas se distribuam aleatoriamente, a robustez do código R-S cai significativamente. Probabilidade de Erro e Desempenho Para modulações específicas, é possível relacionar a probabilidade de erro de bit, PB, com a probabilidade de erro de símbolo, PE. Por exemplo, para modulação MFSK como M = 2m, essa relação é [10]: PB / PE = 2m–1 / 2m – 1 (7) Para os códigos R-S, a probabilidade de erro é uma função que decresce exponencialmente com o comprimento do bloco n, enquanto que a complexidade de decodificação é proporcional a uma baixa potência de n. O aumento da redundância de um código R-S implica o aumento da complexidade da implementação, particularmente para dispositivos de alta velocidade, mas traz o benefício de melhor taxa de erro de bits. Em sistemas de telecomunicações digitais, é errado imaginar que o desempenho do código R-S melhora monotonicamente com o aumento da redundância (ou redução da taxa de código). Na realidade, para cada tipo de modulação e canal de comunicação há uma taxa de código ótimo; isso se explica porque, para altas taxas de código, há pouca redundância e, consequentemente, altas taxas de erros; já quando a taxa de código é muito baixa, num sistema de comunicação em tempo real, menor será a energia por símbolo, o que acaba degradando o desempenho. Para um canal Gaussiano, a taxa de código ótima fica entre 0,6 e 0,7 [10]. 6 Correção de Erros I: Campos Finitos Campos Finitos ou Campos de Galois Para entender os princípios de codificação e decodificação de códigos não-binários, é necessária uma introdução aos campos finitos conhecidos como Campos de Galois (Galois Fields, GF). Um campo de GF(n) é um conjunto com n elementos, fechado com relação às operações de adição e multiplicação, e para qual todo elemento tem seu inverso aditivo e multiplicativo (com exceção para o 0, que não tem inverso multiplicativo). Dado um número primo p, existe um campo de Galois GF(p) com exatamente p elementos. Por exemplo, o campo GF(2) = {0, 1} é um campo de Galois, pois tem dois elementos e, realizando a adição e multiplicação módulo 2, ou seja, tomando o resto da divisão do resultado por 2, o conjunto é fechado. De modo similar, para p primo, GF(p) pode ser representado pelo conjunto {0, 1, 2,..., p – 1}, e as operações aritméticas são realizadas em módulo p. Esse campo pode ser estendido para pm elementos, onde m é um inteiro estritamente positivo, resultando no campo estendido GF(pm). Os códigos R-S utilizam símbolos do campo de extensão GF(2m). Nota: Na prática, basta definir a adição como uma operação de OU-exclusivo lógico e a multiplicação como um E-lógico. À exceção do elemento nulo, todos os elementos em GF(2m) podem ser representados por uma potência de um símbolo básico, a. Como GF(2m) deve ter exatamente 2m elementos, resulta que: (8) Já a propriedade de fechamento implica que: (9) Os códigos R-S utilizam os símbolos do campo de extensão da Equação 8. Representação Polinomial A representação de cada elemento não nulo como uma potência de a é conhecida como representação exponencial. A equivalência de cada elemento com uma palavra binária resulta em sua representação polinomial. Nessa representação, os elementos de GF(2m) são inteiros de 0 a 2m–1. Um elemento não nulo de GF(2m) pode ser representado por um polinômio de grau menor ou igual a m – 1. Ou seja, para i = 0, 1, 2,...,2m – 2, resulta: (10) Onde cada um dos coeficientes pertence a GF(2) (ou seja, é binário). Assim sendo, cada elemento de GF(2m) tem um número binário equivalente da forma: 7 (11) Adição e Multiplicação no Campo de Extensão O campo de extensão GF(2m) é fechado em relação à adição e multiplicação, ou seja, a soma ou produto de dois elementos de GF(2m) resulta sempre num elemento do mesmo campo. Essas operações são necessárias para cálculo dos símbolos de paridade e das síndromes, utilizadas no processo de decodificação. A adição de dois elementos quaisquer de um campo finito é definida como a soma módulo-2 (ou seja, uma operação de ou-exclusivo) dos coeficientes de mesma potência dos polinômios correspondentes a cada elemento, ou seja, na forma polinomial: (12) Se os elementos forem expressos na forma exponencial, é necessário primeiro convertê-los para forma polinomial antes de proceder a adição. Já em relação à multiplicação, a condição de fechamento implica que: (13) Portanto, a multiplicação de dois elementos resulta num elemento cujo expoente é obtido dividindo-se a soma dos expoentes dos elementos por 2m – 1 e tomando o resto, ou seja, na forma exponencial: (14) Se os fatores forem expressos na forma polinomial, é necessário então convertê-los na forma exponencial e então efetuar o produto conforme a Equação 14. Polinômios Primitivos Um campo de Galois GF(2m) é definido por um polinômio primitivo f(X) de ordem m. Para gerar os elementos de GF(2m) a partir de f(X), começamos com os elementos 0, 1 e X, e seguimos multiplicando o último elemento por X, tomando o resto da divisão do resultado por f(X), ou seja, resultado módulo f(X). Esse procedimento continua até que o conjunto tenha exatamente 2m elementos. Um polinômio irredutível (que não pode ser fatorado) f(X) de ordem m é primitivo se e somente se o menor número inteiro positivo n para o qual f(X) é um divisor de Xn + 1 para n = 2m – 1. A tabela 2 mostra alguns exemplos de polinômios primitivos. Um grande número de exemplos de polinômios primitivos pode ser encontrado em [8]. m 2 3 3 4 Tabela 2: Exemplos de polinômios primitivos f(X) m f(X) 1 + X + X2 5 1 + X + X3 + X4 + X5 1 + X2 + X3 5 1 + X + X2 + X4 + X5 1 + X + X3 6 1 + X5 + X6 1 + X3 + X4 6 1 + X + X6 8 4 1 + X + X4 6 5 1 + X3 + X5 6 5 1 + X2 + X5 6 5 1 + X + X2 + X3 + X5 1 + X2 + X3 + X4 + X5 6 5 1 + X + X4 + X5 + X6 1 + X + X2 + X5 + X6 1 + X + X3 + X4 + X6 1 + X2 + X3 + X5 + X6 Um polinômio primitivo f(X) de ordem m pode ser representado como: f(X) = f0 + f1 X + f2 X2 + ... + fm-1 Xm-1 + fm Xm (15) Onde cada coeficiente fi é binário, além disso, sempre f0 = 1 (caso contrário, o polinômio seria fatorável por X e, portanto, não seria irredutível, nem primitivo) e fm = 1 (caso contrário, não teria ordem m). Ou seja, a Equação 16 pode ser reescrita como: f(X) = 1 + f1 X + f2 X2 + ... + fm-1 Xm-1 + fm Xm (16) É também interessante notar que, se um polinômio f(X) for primitivo, seu recíproco também o será. Ou seja, também é primitivo o polinômio: f(X) = 1 + fm-1 X + fm-2 X2 + ... + f2 Xm-2 + f1 Xm-1 + Xm (17) Tomemos, como exemplo, o campo finito GF(24) definido pelo polinômio primitivo f(X) = 1 + X + X4, extraído da tabela 2. Como m = 4, o campo terá um total de 24 = 16 elementos, cada qual com 4 bits; esses elementos serão os símbolos usados pelo código R-S. Cada elemento pode ser expresso em função dos elementos-base {X0, X1, X2, X3}. O elemento nulo e os 4 primeiros não-nulos ({a0, a1, a2, a3}) são expressos segundo a Equação 11 de forma trivial: 0 = 0X0 + 0X1 + 0X2 + 0X3 (18) a0 = 1X0 + 0X1 + 0X2 + 0X3 (19) a1 = 0X0 + 1X1 + 0X2 + 0X3 (20) a2 = 0X0 + 0X1 + 1X2 + 0X3 (21) a3 = 0X0 + 0X1 + 0X2 + 1X3 (22) Para obter as representações binárias dos demais elementos ({a4, a5, ... , a14}) é necessário utilizar o polinômio primitivo. Primeiramente, definindo a como raiz do polinômio primitivo, tem-se que: 9 (23) Onde se usou o fato de que, na aritmética binária, +1 = –1. Para obter a5, usa-se a Equação 23, resultando em: (23) Os próximos elementos são obtidos de forma similar: Só a título de confirmação, o que já se esperava da Equação 13: (34) Lembrando novamente que, segundo a Equação 11, cada elemento de GF(24) pode ser expresso como um polinômio de ordem igual ou inferior a 3; os resultados obtidos nas Equações 18 - 33 podem ser sumarizados na tabela 3. Tabela 3: Mapeamento dos elementos do campo GF(16) gerado pelo polinômio f(X) = 1+X+X4 Representação Coeficiente Representação Exponencial Polinomial Hexadecimal X0 0 1 X1 0 0 X2 0 0 X3 0 0 0 1 a1 0 1 0 0 2 a2 0 0 1 0 4 a3 0 0 0 1 8 a4 1 1 0 0 3 0 a 0 10 a5 0 1 1 0 6 a6 0 0 1 1 12 a7 1 1 0 1 11 a8 1 0 1 0 5 a9 0 1 0 1 10 a10 1 1 1 0 7 a11 0 1 1 1 14 a12 1 1 1 1 15 a13 1 0 1 1 13 a14 1 0 0 1 13 Para campos de pequena extensão, como o GF(24) analisado, é conveniente sumarizar os resultados das somas e produtos de dois elementos quaisquer do campo em tabelas, como as ilustradas nas tabelas 4 e 5. 0 Tabela 4: Tabela de adição para o campo GF(16) gerado pelo polinômio f(X) = 1 + X + X4 0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a0 a14 a14 a0 a0 0 a1 a1 a4 a4 0 a2 a2 a8 a3 a3 a4 + a8 a14 a1 a10 a13 a9 a2 a7 a5 a12 a11 a6 a3 a9 a0 a2 a11 a14 a10 a3 a8 a6 a13 a12 a7 a5 a5 0 a10 a1 a3 a12 a0 a11 a4 a9 a7 a14 a13 a14 a9 a6 a6 0 a11 a2 a4 a13 a1 a12 a5 a10 a8 a0 a4 a1 a0 a10 a7 a7 0 a12 a3 a5 a14 a2 a13 a6 a11 a9 a5 a5 a10 a2 a1 a11 a8 a8 0 a13 a4 a6 a0 a3 a14 a7 a12 a6 a6 a13 a11 a3 a2 a12 a9 a9 0 a14 a5 a7 a1 a4 a0 a8 a7 a7 a9 a14 a12 a4 a3 a13 a10 a10 0 a0 a6 a8 a2 a5 a1 a8 a8 a2 a10 a0 a13 a5 a4 a14 a11 a11 0 a1 a7 a9 a3 a6 a9 a9 a7 a3 a11 a1 a14 a6 a5 a0 a12 a12 0 a2 a8 a10 a4 a10 a10 a5 a8 a4 a12 a2 a0 a7 a6 a1 a13 a13 0 a3 a9 a11 a11 a11 a12 a6 a9 a5 a13 a3 a1 a8 a7 a2 a14 a14 0 a4 a10 a12 a12 a11 a13 a7 a10 a6 a14 a4 a2 a9 a8 a3 a0 a0 0 a5 a13 a13 a6 a12 a14 a8 a11 a7 a0 a5 a3 a10 a9 a4 a1 a1 0 a14 a14 a3 a7 a13 a0 a9 a12 a8 a1 a6 a4 a11 a10 a5 a2 * 0 a0 Tabela 5: Tabela de multiplicação para o campo GF(16) gerado pelo polinômio f(X) = 1+X+X4 0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 0 a a a a a a a a a a a a a a13 a2 0 a14 0 a14 11 a1 0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a0 a2 0 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a0 a1 a3 0 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a0 a1 a2 a4 0 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a0 a1 a2 a3 a5 0 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a0 a1 a2 a3 a4 a6 0 a6 a7 a8 a9 a10 a11 a12 a13 a14 a0 a1 a2 a3 a4 a5 a7 0 a7 a8 a9 a10 a11 a12 a13 a14 a0 a1 a2 a3 a4 a5 a6 a8 0 a8 a9 a10 a11 a12 a13 a14 a0 a1 a2 a3 a4 a5 a6 a7 a9 0 a9 a10 a11 a12 a13 a14 a0 a1 a2 a3 a4 a5 a6 a7 a8 a10 0 a10 a11 a12 a13 a14 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a11 0 a11 a12 a13 a14 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a12 0 a12 a13 a14 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a13 0 a13 a14 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a14 0 a14 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 Circuito Gerador Para campos de Galois de alta dimensão, a determinação da representação polinomial de cada elemento por meios algébricos é inviável. No caso geral, o mapeamento dos elementos do campo GF(2m) da forma exponencial para forma polinomial pode ser realizado de forma mais genérica utilizando a idéia de um circuito com registradores de deslocamento com realimentação linear (Linear Feedback Shift Register, LFSR). Esse circuito consiste em m flip-flops em cascata, com realimentação do bit mais significativo. A realimentação ou não desse bit reflete exatamente os coeficientes do polinômio primitivo, conforme a Equação 15. O estado dos registradores num dado instante dá a representação polinomial de um elemento do campo de Galois. Para o campo GF(16) analisado como exemplo nesta seção, o circuito LFSR é ilustrado na figura 2. Para gerar os elementos do campo, à exceção do elemento nulo, basta iniciar os registradores com um elemento qualquer e aplicar pulsos ao circuito; a cada pulso, um novo estado é gerado. Obviamente, o circuito tem natureza periódica (cíclica), e o procedimento termina quando o estado inicial se repetir. Figura 2: Circuito baseado em registrador de deslocamento com realimentação para geração dos elementos do campo de Galois GF(16) gerado pelo polinômio f(X) = 1 + X + X4 As conexões de realimentação refletem os coeficientes do polinômio primitivo. 12 Correção de Erros I: Codificação e Decodificação Codificação Em sua forma mais comum, os códigos de Reed-Solomon podem ser expressos conforme a Equação (35): (n, k) = (2m-1,2m – 1 – 2t) (35) Onde o número de símbolos de paridade é dado por n – k = 2t e t representa a capacidade de correção de erros de símbolo do código. Os códigos R-S são um subconjunto dos códigos BCH (Bose, Chaudhuri e Hocquenghem) e, portanto, podem ser gerados por um polinômio de grau igual ao número de símbolos de paridade, ou seja: g(X) = g0 + g1X + g2X2 + ... + g2t–1X2t–1 + X2t (36) Onde cada um dos coeficientes é um elemento do campo GF(2m). Como o grau desse polinômio é 2t, há exatamente 2t sucessivas potências de a que são raízes de g(X). Portanto, o polinômio gerador pode ser construído a partir do polinômio primitivo que define o campo através de: g(X) = (X – a)(X – a2)(X – a3) ... (X – a2t) (37) Onde as operações aritméticas em a devem obedecer às tabelas para o campo de extensão. Codificação na Forma Sistemática O processo de codificação de códigos R-S é análogo ao dos códigos cíclicos binários. O polinômio da mensagem a ser codificada, m(X), é deslocado para os k estágios mais à direita de um registrador da palavracódigo, e então o polinômio contendo os símbolos de paridade, p(X), é adicionado nos n – k estágios à esquerda. Matematicamente, o polinômio correspondente à palavra-código pode ser escrito como U(X) = p(X) + Xn–k m(x) (38) onde a multiplicação por Xn–k equivale a deslocar a mensagem para a direita de n–k posições. O polinômio de paridade pode ser obtido dividindo-se a mensagem deslocada pelo polinômio gerador e tomando o resto, ou seja, p(X) = Xn–k m(X) modulo g(X) (39) Codificação Sistemática Usando Registradores de Deslocamento O processo de codificação pode ser implementado de maneira direta utilizando-se a idéia de registradores de deslocamento com realimentação. O processo é similar ao usado para gerar os elementos do campo de extensão, mas agora cada estágio do registrador armazena um símbolo, e não um bit e, de maneira similar, cada coeficiente do polinômio gerador é um símbolo também. O circuito é ilustrado na figura 3, tem um registrador de deslocamento com n – k = 2t estágios e duas chaves, e seu funcionamento pode ser descrito da seguinte maneira: 13 Inicialmente os registradores são todos zerados. Durante os k primeiros ciclos do sinal de relógio, a chave S1 é fechada, de modo que os símbolos de entrada vão sendo carregados nos registradores de deslocamento. À medida que os símbolos de entrada vão sendo deslocados, os símbolos de paridade vão ser formando nos registradores. Durante esses ciclos, a chave S2 fica na posição para baixo, de modo que os símbolos de entrada vão também sendo direcionados à saída. Após o k-ésimo símbolo da mensagem original ter sido transferido para a saída, a chave S1 é aberta e a chave S2 movida para a posição para cima. Durante os próximos n – k ciclos, os símbolos de paridade contidos nos registradores são movidos para a saída e os registradores são zerados novamente. O total de número de ciclos de relógio é de n e o conteúdo do registrador de saída será U(X) = p(X) + Xn–k m(X), onde p(X) representa os símbolos de paridade e m(X) denota a mensagem original em forma polinomial. Figura 3: Codificador Reed-Solomon. Durante os primeiros k ciclos, S1 fica fechada e S2 para baixo, e os símbolos de entrada passam para saída. Então S1 é aberta e S2 passa para a posição para baixo, e durante os próximos n – k ciclos os símbolos de paridade são movidos para saída. Decodificação Em Telecomunicações, a palavra-código produzida pelo codificador pode ser corrompida por erros durante os processos de transmissão; alternativamente, no caso de sistemas de memória podem acontecer erros durante o armazenamento e/ou recuperação de dados. O padrão de erros pode ser entendido como uma palavra de n símbolos que é adicionada à palavra-código. Esse padrão de erros pode ser representado por: (40) Onde cada termo ei é um símbolo de GF(2m). A palavra corrompida (ou recebida) pode sr representada pela soma da palavra-código com o padrão de erros, ou seja: r(X) = U(X) + e(X) (41) Dada a palavra recebida, se o padrão de erro puder ser determinado, a palavra-código pode ser determinada 14 por U(X) = r(X) + e(X) (42) E, portanto, o problema de decodificação é, a partir da palavra recebida, determinar o padrão de erro. Observe que, para cada termo não nulo na Equação 40, há duas incógnitas: a localização do erro (o índice i na equação) e sua magnitude (o termo ei). Contrastando com o caso de decodificação binária, o problema é significativamente mais complexo, pois, no caso binário, conhecendo a localização de um erro, basta inverter o bit na localização para corrigir a palavra recebida; já com codificação não binária, como é o caso do código Reed-Solomon, é também necessário determinar a magnitude do erro para possibilitar a correção. Portanto, para decodificar uma palavra corrompida com v <= t erros, são necessárias 2v equações. Arquitetura do Processo de Decodificação O processo de decodificação pode ser dividido em etapas que devem ser executadas em sequência. O procedimento adotado neste trabalho é ilustrado na figura 4. Figura 4: Fluxo do processo de decodificação utilizando o algoritmo de Berlekamp-Massey, o procedimento de Chien e a fórmula de Forney. Inicialmente, a partir da palavra recebida, as síndromes são calculadas. A partir das síndromes, conforme explicado abaixo, são determinados o polinômio localizador de erros e o polinômio avaliador de erros, utilizando o algoritmo de Berlekamp-Massey [6]. Em seguida, utilizando o procedimento de Chien, as posições dos erros são determinadas. Finalmente, usando a fórmula de Forney, as magnitudes dos erros são determinadas e a palavra-código original é restaurada. Obviamente, caso as síndromes sejam todas nulas, considera-se que não houve erro e a palavra recebida é tida como a palavra-código original; além disso, caso o número de erros exceda a capacidade de correção do código, a palavra-código obtida ao final do processo não corresponderá à palavra-código original. Cálculo das Síndromes 15 Uma síndrome é o resultado de um teste de paridade sobre a palavra recebida r, determinando se ela pertence ou não ao conjunto de palavras-código válidas. Se r for uma palavra-código, então todas as síndromes devem ter o valor 0. Há n – k síndromes, frequentemente representadas como um vetor S = {si} para i = 1, ... , n–k. Como, para toda palavra-código válida, vale a relação: U(X) = m(X) g(X) (43) Segue que todas as raízes do polinômio gerador do código, g(X), também são raízes de U(X). Consequentemente, da Equação 41 temos que, se r for uma palavra-código válida (em outras palavras, não houve erros), então o polinômio r(X) avaliado para cada uma das raízes de g(X) deve resultar em 0. As síndromes são calculadas por: (44) É conveniente definir um polinômio de síndromes como: (45) Embora o polinômio s(X) tenha um número infinito de termos, a relação da Equação 44 somente é válida para os primeiros n – k termos. Localização e Magnitude dos Erros Suponhamos que a palavra-código tenha sido corrompida por v <= t erros nas localizações Xj1, Xj2, ... , Xjv. Assim sendo, a Equação 40 pode ser escrita somente com os termos não-nulos, como se segue: (46) O índice 1 <= j <= n dá a posição de cada erro, e os subíndices j1, j2, ... , jv são somente contadores. Por conveniência, usamos a seguinte definição: Definição 1: Um número localizador de erro é definido como: Para simplificar a notação, define-se também: Definição 2: A magnitude de um erro é definida como: Usando as Definições 1 e 2, as n–k = 2t equações para determinação das síndromes podem ser escritas como: 16 (47) Embora as síndromes definam um conjunto de 2t equações com 2t incógnitas (t localizações de erros e t magnitudes), o sistema não é trivial porque, com exceção da primeira, todas são não lineares. Qualquer procedimento que resolva esse conjunto de equações pode ser chamado de um decodificador de Reed-Solomon. Algoritmo de Berlekamp-Massey Vários algoritmos para implementação do decodificador têm sido propostos na literatura. Por exemplo, Blahut concebeu configurações originais de decodificadores que não requerem o cálculo das síndromes [1]. Wei e Wei propuseram um algoritmo rápido que pode ser implementado num único chip em VLSI [11]. Clarke [2] constrói o polinômio localizador de erros utilizando o algoritmo Euclidiano e até propõe uma implementação em hardware. Para os códigos cíclicos conhecidos como códigos BCH (Bose-ChaudhuriHocquenghem) em geral e Reed-Solomon em particular, Huffman e Pless apresentam, após longa e detalhada exposição teórica, quatro algoritmos de decodificação [5]: Algoritmo de Peterson-Gorenstein-Zierler [3], aplicado numa sequência de quatro passos, na qual o segundo passo é notadamente o mais complexo e que requer mais tempo de processamento; Algoritmo de Berlekamp-Massey [7], que fornece uma maneira mais eciente de executar o segundo passo do algoritmo de Peterson-Gorenstein-Zierler; Algoritmo de Sugiyama-Kasahara-Hirasawa-Namekawa [12], que através da aplicação do algoritmo Euclidiano para polinômios, sugere uma outra maneira de executar o segundo passo de PetersonGorenstein-Zierler de forma mais eficiente; Algoritmo de Sudan-Guruswami [4], que implementa a decodificação para além da distância mínima do código e produz uma lista de todas as palavras-código a certa distância da palavra recebida. Neste trabalho, optou-se por implementar o decodificador utilizando o algoritmo de Berlekamp-Massey, por sua eficiência e relativa facilidade de implementação. Tomou-se o cuidado, porém, de deixar a estrutura das bibliotecas de software flexível o suficiente para que outros algoritmos possam ser naturalmente adaptados ao simulador. Polinômios de Localização e Avaliação de Erros A determinação das posições e magnitudes dos erros é realizada indiretamente. Primeiro, dois polinômios são definidos conforme abaixo. Definição 3: O polinômio localizador de erros é dado por: (48) 17 Portanto, as raízes do polinômio são os inversos dos localizadores de erro, ou seja: Definição 4: O polinômio avaliador de erros é definido por: (49) O polinômio localizador de erros está relacionado com o polinômio avaliador de acordo com uma expressão conhecida como Equação-Chave. O algoritmo de Berlekamp-Massey dá um procedimento iterativo para determinação dos polinômios definidos nas Equações 48 e 49. Para obtenção da equação-chave, primeiramente, das definições acima, temos: (50) Usando a expansão em série: (51) A Equação 50 se torna: (52) Rearranjando os somatórios, teremos: (53) Substituindo a Equação 47 na expressão acima, teremos, finalmente, a equação-chave usando a definição da Equação 45: Sumarizando, temos a seguinte definição. Definição 5: A equação-chave da decodificação de Reed-Solomon usando o algoritmo de BerlekampMassey é: Onde s(X) tem, no máximo, 2t coeficientes não-nulos e os polinômios localizador e avaliador de erro devem 18 ter grau de, no máximo, t, para possibilitar a decodificação. Não há como computar esses polinômios diretamente, e o algoritmo de Berlekamp-Massey dá um procedimento iterativo de 2t passos para calculá-los. Na l-ésima etapa é determinado o par de polinômios de grau l, para l = 1, 2, ... , 2t. O último par de polinômios, de grau 2t, é o par de polinômios que se desejava calcular originalmente. A cada etapa, a equação-chave deve ser satisfeita, ou seja: (54) Idealmente, os polinômios encontrados numa etapa também deveriam satisfazer a equação chave da etapa seguinte, ou seja: (55) Pois, caso isso acontecesse, teríamos o m do procedimento iterativo. Contudo, isso normalmente não ocorre e temos a seguinte relação: (56) Onde D1(l) é o coeficiente não-nulo de Xl+1 do produto polinomial [1 + s(X)]s(l)(X). Polinômios Auxiliares Para permitir o cálculo de (s(l+1)(X), w(l+1)(X)) a partir de (s(l)(X), w(l)(X)), dois outros polinômios, (t(l)(X), g(l)(X)), com grau menor ou igual a l, são introduzidos, de modo que respeitem a seguinte equação: (57) Usando os polinômios auxiliares, derivam-se as equações para obtenção de s(l+1)(X) e w(l+1)(X) a partir de s(l)(X), w(l)(X), t(l)(X) e g(l)(X): (58) (59) As equações acima garantem que o novo par (s(l+1)(X), w(l+1)(X)) satisfaça a equação-chave. As Equações 58 e 59 são fáceis de implementar na prática, pois requerem somente uma multiplicação por escalar (D1(l)), um deslocamento de coeficientes (multiplicação por X) e uma subtração de polinômios. Para atualizar os polinômios auxiliares, há dois conjuntos de regras possíveis: 19 Tanto as regras A (Equações 60 e 61) quanto as B (Equações 62 e 63) são facilmente implementáveis em hardware ou software; as regras A exigem somente um deslocamento de coeficientes, enquanto que as regras B implicam uma divisão (ou multiplicação) dos coeficientes de um polinômio por um escalar. O conjunto apropriado de regras é escolhido de modo a minimizar os graus dos polinômios t(l+1)(X) e g(l+1)(X). Pode-se demonstrar que, qualquer que seja a escolha, teremos: (64) A escolha do conjunto apropriado de regras depende da análise dos graus dos polinômios s(l)(X), w(l)(X), t(l)(X) e g(l)(X), o que não é trivial, pois devem ser considerados os casos em que termos são cancelados acidentalmente. Para facilitar essa análise, Berlekamp e Massey introduziram um termo D(l) que determina os limites superiores dos graus dos polinômios, de modo que: (65) (66) As regras A são usadas quando D1(l) = 0 ou D(l) > (l+1) / 2. As regras B serão usadas quando D1(l) <> 0 ou D(l) < (l+1) / 2. Quando as regras A forem empregadas, D(l) não muda, ou seja, D(l+1) = D(l); quando forem usadas as regras B, fazemos D(l+1) = l + 1 – D(l). Essas escolhas garantem que 0 <= D(l+1) <= l+1, que os graus de s(l+1)(X) e w(l+1)(X) sejam limitados por D(l+1), e que os graus de t(l+1)(X) e g(l+1)(X) sejam limitados por 1 – D(l+1). Além disso, pode-se verificar que 0 <= D(l) <= l. As condições acima não incluem o caso em que D1(l) <> 0 ou D(l) < (l+1) / 2. Neste caso, pode ser provado que tanto as regras A como as regras B garantem que os limites impostos nas Equações 65 e 66 sejam respeitadas. Berlekamp e Massey sugerem que, através da introdução de um novo parâmetro binário, B(l), pode-se restringir ainda mais os limites sobre os graus dos polinômios t(l)(X) e g(l)(X). Com esse novo parâmetro, as regras A são usadas quando D1(l) <> 0 ou D(l) < (l+1) / 2 e B(l) = 0 e, nesse caso, B(l+1) = B(l); caso D1(l) <> 0 ou D(l) < (l+1) / 2 e B(l) = 1, as regras B são usadas e faz-se B(l+1) = 1 – B(l). Essas regras garantem que: (67) (68) O procedimento descrito acima limita os graus dos polinômios localizador e avaliador de erros conforme o 20 lema abaixo, apresentado sem demonstração. Lema 1: A cada iteração, os graus dos polinômios localizador e avaliador de erros são limitados por: (69) (70) Como o procedimento de Berlekamp-Massey tem n – k = 2t etapas, o lema acima garante que, no final, s(2t)(X) = s(X) tenha, no máximo, grau t. O mesmo acontece com w(X). Para completar o algoritmo, as seguintes condições iniciais são definidas: (71) O fluxograma completo do algoritmo é ilustrado na figura 5, onde a decisão entre as regras A e B pode ser sumarizada em: (72) 21 Figura 5: Fluxograma do algoritmo de Berlekamp-Massey Procedimento de Busca de Chien O algoritmo de Berlekamp-Massey permite determinar os polinômio localizador e avaliador de erros, ou seja, s(X) e w(X). Da Equação 48, tem-se que os localizadores de erros são os recíprocos das raízes de s(X). Em vez de determinar as raízes de s(X), Chien propôs uma maneira mais eficiente de determinar os localizadores de erros através de um procedimento enumerativo descrito na figura 6. 1. Inicializar conjunto de localizadores como o conjunto vazio, ou seja, {bl}¬ Ø 2. l¬0 3. Repetir 4. l¬l+1 5. Se s(al = 0), então incluir localizador al em {bl} 6. Até que o número de localizadores seja igual à ordem de s(X) Figura 6: Procedimento de busca de Chien 22 Como o procedimento de Chien dá diretamente os índices em que os erros ocorreram, não há necessidade de computar o logaritmo discreto. Além disso, o procedimento pode ser ainda otimizado se os coeficientes de s(al+1) forem calculados a partir dos coeficientes de s(al). Lembrando que: (73) Temos que: (74) E, portanto: (75) Comparando as Equações 74 e 75, percebe-se que o i-ésimo coeficiente de s(al+1) pode ser obtido rapidamente multiplicando-se o coeficiente correspondente de s(al) por ai. Fórmula de Forney Tendo calculado as posições dos erros, resta determinar suas magnitudes, Yl. Forney notou que a magnitude de cada erro pode ser determinada computando o valor do polinômio avaliador de erros, w(X), para X = bl–1, ou seja: (76) Finalmente, a magnitude de um erro pode ser calculada como (77) Com as posições e magnitudes dos erros determinadas, a palavra-código original pode ser restaurada pela Equação 42, finalizando o processo de decodificação. 23 Correção de Erros I: Considerações finais Este tutorial parte I procurou apresentar os fundamentos teóricos dos códigos Reed-Solomon, detalhando suas características, aspectos teóricos dos campos finitos e a codificação e decodificação desses códigos. O tutorial parte II apresentará inicialmente a biblioteca desenvolvida em linguagem de programação Java contendo as classes para realização de álgebra de Campos de Galois, e a seguir apresentará o simulador propriamente dito e as conclusões do trabalho realizado. Referências [1] Richard E. Blahut. A universal reed-solomon decoder. IBM Journal of Research and Development, 28(2):150-158, March 1984. [2] C. K. P. Clarke. Reed-solomon error correction. Technical report, BBC Research & Development, 2002. [3] D. C. Gorenstein and N. Zierler. A class of error-correcting codes in pm symbols. Journal of SIAM, 9:207-214, 1961. [4] V. Guruswami and M. Sudan. Improved decoding of reed-solomon and algebraic-geometry codes. IEEE Transactions on Information Theory, 45:1757 1767, 1999. [5] W. Cary Huffman and Vera Pless. Fundamentals of Error Correcting Codes. Cambridge University Press, 2003. [6] Bruce Maggs. Decoding reed-solomon codes. Lecture notes, October 2000. [7] J. L. Massey. Shift-register synthesis and bch decoding. IEEE Transactions on Information Theory, 15:122-127, 1969. [8] Todd K. Moon. Error Correction Coding: Mathematical Methods and Algorithms. Wiley- Interscience, 2005. [9] I. S Reed and G. Solomon. Polynomial codes over certain finite fields. SIAM Journal of Applied Math., 8:300-304, 1960. [10] Bernard Sklar. Digital Communications: Fundamentals and Applications. Prentice-Hall, 2 edition, 2001. [11] Shyue-Win Wei and Che-Ho Wei. High-speed decoder of reed-solomon codes. IEEE Transactions on Communications, 11(41):1588-1593, November 1993. [12] S. Hirasawa Y. Sugiyama, M. Kasahara and T. Namekawa. A method of solving a key equation for decoding goppa codes. Information and Control, 27:87-99, 1975. 24 Correção de Erros I: Teste seu entendimento 1. Qual a finalidade dos códigos Reed-Solomon? Implementar códigos de correção de erros em sistemas digitais de comunicação. Implementar criptografia em sistemas de segurança. Implementar geradores de códigos aleatórios. Implementar reparadores de arquivos criptografados. 2. Qual é o conceito necessário para entender os princípios de codificação e decodificação de códigos não-binários? Conceito de aritmética não binária. Conceito de transmissão analógica. Conceito de campos finitos conhecidos como Campos de Galois (Galois Fields, GF). Conceito de geometria espacial. 3. Como para os campos de Galois de alta dimensão, a determinação da representação polinomial de cada elemento por meios algébricos é inviável, qual é a alternativa utilizada? Um supercomputador com gerador de polinômios aleatórios. Um circuito analógico com gerador de ruídos. Um sistema de n equações com n-1 incógnitas. Um circuito gerador de polinômios com registradores de deslocamento com realimentação linear (Linear Feedback Shift Register, LFSR). 25