Unidad 5 · Los códigos cíclicos · codificar en forma sistemática es una sola división, el registro de n − k etapas que la hace un bit por ciclo, y el de k etapas por h(X), que gana cuando hay más paridad que mensaje
En un código lineal cualquiera, codificar es multiplicar el mensaje por la matriz generadora: en forma sistemática, cada uno de los bits de paridad es una suma sobre los bits del mensaje, y hay que guardar coeficientes. En un código cíclico alcanza con dividir por un polinomio, y la división la hace un registro de desplazamiento con tantas etapas como bits de paridad. En el (15,5) son 10 etapas contra 50 coeficientes; y hay otro registro, de 5, que da las mismas palabras.
Sea un código cíclico de polinomio generador , de grado , y un mensaje . La palabra es del código, pero el mensaje no aparece en ella: habría que dividir para leerlo. La forma sistemática sale en tres pasos.
Paso 1: correr el mensaje. Se multiplica por , que lo lleva a las posiciones de mayor orden y deja libres las de abajo.
Paso 2: dividir por .
Paso 3: sumar el resto. Sobre GF(2) sumar y restar es lo mismo, así que es un múltiplo de de grado menor que : una palabra del código. Queda
con
Codificar en forma sistemática es una sola división, y de ella sólo interesa el resto. Escrita como vector, con la posición 0 a la izquierda, la palabra lleva la paridad primero y el mensaje al final: al revés que la de la Unidad 3, que ponía el mensaje adelante.
En el (7,4) de , con , o sea , el corrimiento da . Módulo vale , y de ahí, multiplicando por y reduciendo cada vez,
Sumando, , la paridad es y la palabra , que es .
La división no se hace con lápiz: la hace un registro de desplazamiento realimentado. Tiene etapas, , que arrancan en cero, y una conexión por cada coeficiente no nulo de por debajo del mayor. El mensaje entra un bit por ciclo, empezando por , el de mayor orden. En cada ciclo, con el bit que entra:
El bit es la realimentación: la entrada más lo que sale por la última etapa. Que eso calcule el resto se ve siguiendo qué guarda el registro.
Paso 1: lo que guarda. Sea el polinomio de los bits que ya entraron. La afirmación es que, después de ciclos, el registro tiene , leído como . Con es el cero del arranque.
Paso 2: un ciclo es multiplicar por y sumar la entrada. Como , hay que calcular módulo . Multiplicar por corre cada etapa un lugar, y el coeficiente de arriba pasa a , donde también cae la entrada: ahí queda . Por eso el mensaje entra «por arriba»: es lo que antes se hacía multiplicando por .
Paso 3: se reemplaza por lo que vale. Módulo , . Si , se suman esos coeficientes a las etapas corridas; si , no se suma nada. Con es exactamente la regla de arriba.
Paso 4: después de ciclos. Ya entró el mensaje entero, , y el registro tiene : la paridad.
La operación tiene dos fases. En los ciclos 1 a la compuerta de la realimentación está abierta, los bits del mensaje entran al registro y, a la vez, salen al canal: es la parte sistemática. En los ciclos a la compuerta se cierra, el registro se corre sin realimentar y descarga la paridad, de a . Al canal sale primero y último. El (7,4) con , ciclo a ciclo:
| ciclo | entra | f | b₀ b₁ b₂ |
|---|---|---|---|
| 0 | — | — | 000 |
| 1 | u₃ = 1 | 1 | 110 |
| 2 | u₂ = 1 | 1 | 101 |
| 3 | u₁ = 0 | 1 | 100 |
| 4 | u₀ = 1 | 1 | 100 |
Las cuatro veces la realimentación vale 1, aunque en el ciclo 3 entra un cero: ahí lo que realimenta es la última etapa. El registro termina en , que es el de la cuenta a mano.
El costo es biestables y un sumador módulo 2 por conexión, y crece linealmente con la redundancia. Contra los coeficientes del producto por la matriz, es la primera ganancia concreta de que el código sea cíclico: 3 etapas contra 12 en el (7,4), 10 contra 50 en el (15,5), 11 contra 132 en el Golay (23,12).
El (15,5) de la clase práctica tiene , así que el registro tiene diez etapas y seis conexiones, una por cada coeficiente no nulo por debajo de : las del , , , , y . Con , o sea , los bits entran en el orden , que es 0, 1, 1, 0, 1:
| ciclo | entra | f | b₀ … b₉ |
|---|---|---|---|
| 0 | — | — | 0000000000 |
| 1 | u₄ = 0 | 0 | 0000000000 |
| 2 | u₃ = 1 | 1 | 1110110010 |
| 3 | u₂ = 1 | 1 | 1001101011 |
| 4 | u₁ = 0 | 1 | 1010000111 |
| 5 | u₀ = 1 | 0 | 0101000011 |
El primer ciclo no cambia nada: entra un cero en un registro de ceros. El segundo carga los coeficientes de sin el , que es lo que vale módulo . En el quinto la entrada y la última etapa valen 1 las dos, se cancelan, y el registro sólo se corre.
Para comprobarlo sin el registro, se divide. El corrimiento es , y cada potencia se reduce módulo :
La suma, coeficiente por coeficiente, es , el del ciclo 5, y la palabra es
El registro de no es el único. Con el polinomio de paridad , de grado , sale otro de etapas.
Paso 1: el producto por . Toda palabra es con , así que . El primer sumando vive por debajo de y el segundo desde : los coeficientes de a del producto son cero.
Paso 2: ecuaciones. El coeficiente de , para , es .
Paso 3: despejar. Como , el término de pasa solo al otro lado:
Paso 4: leerla como recurrencia. Con , el bit sale de , que son el mensaje. Con , de los siguientes, que ya incluyen el bit recién calculado. Cada bit de paridad es una suma de los que tiene inmediatamente arriba.
El circuito es un registro de etapas que guarda esa ventana. En los primeros ciclos se carga con el mensaje, desde , que sale al canal a la vez. En los siguientes suma las etapas conectadas según , manda el resultado al canal y lo vuelve a meter por la entrada. La palabra entera pasa por el registro, primero, y cada bit que entra empuja afuera el que ya no hace falta.
En el (7,4), y la recurrencia es . Con en :
La misma paridad , con cuatro etapas en vez de tres. En el (15,5) la cuenta se da vuelta: , cinco etapas y tres derivaciones, y la recurrencia es . Con el mismo , el registro (leído , el de menor índice a la izquierda) y el bit que produce:
| j | registro | sale | j | registro | sale |
|---|---|---|---|---|---|
| 1 | 10110 | v₉ = 1 | 6 | 00011 | v₄ = 0 |
| 2 | 11011 | v₈ = 1 | 7 | 00001 | v₃ = 1 |
| 3 | 11101 | v₇ = 0 | 8 | 10000 | v₂ = 0 |
| 4 | 01110 | v₆ = 0 | 9 | 01000 | v₁ = 1 |
| 5 | 00111 | v₅ = 0 | 10 | 10100 | v₀ = 0 |
Leídos de a , los bits son : la paridad que dejó el registro de diez etapas. No es una coincidencia de este mensaje. Codificados todos los mensajes de los 30 códigos cíclicos de largo 15 —40 926 en total— y de los 6 de largo 7 —114—, la recurrencia por y la división por dan la misma palabra en todos. Y en los 4096 mensajes del Golay (23,12) también.
Las dos realizaciones hacen lo mismo con registros de largo distinto, así que se elige la más corta. Conviene la de cuando hay más bits de paridad que de mensaje, : el (15,5), 5 etapas contra 10, o el (7,3), 3 contra 4. En los códigos de tasa alta, que son los habituales, conviene la de . El Golay (23,12) está del lado de por una etapa: 11 contra 12.
Arranca con el (7,4) y el mensaje de la tabla de la sección 02. Mirá el ciclo 3: entra un cero y la realimentación igual se enciende. Después pasá al (15,5) y comparalo con la tabla de la sección 03: en el primer ciclo no pasa nada y en el quinto la entrada y la última etapa se cancelan. Por último, cambiá al registro por : la mitad de etapas y la misma palabra, bit por bit.
Los dos codificadores, en listas de bits con la posición 0 primero, y la comprobación sobre los 32 mensajes del (15,5):
from itertools import product
def registro_g(u, g):
"""n − k etapas: el mensaje entra desde u_{k−1}; queda b(X)."""
r = len(g) - 1
b = [0] * r
for bit in reversed(u):
f = bit ^ b[-1] # la realimentación
b = [f] + [b[j - 1] ^ (g[j] & f) for j in range(1, r)]
return b + list(u) # paridad primero
def registro_h(u, h, n):
"""k etapas: cada bit de paridad es la suma de los k siguientes, ec. (16)."""
k = len(u)
v = [0] * (n - k) + list(u)
for j in range(1, n - k + 1):
v[n - k - j] = sum(h[i] & v[n - i - j] for i in range(k)) % 2
return v
s = lambda v: "".join(map(str, v))
g = [1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1] # el g(X) del (15,5)
h = [1, 1, 0, 1, 0, 1] # 1 + X + X³ + X⁵
u = [1, 0, 1, 1, 0]
print(s(registro_g(u, g)), s(registro_h(u, h, 15)))
# 010100001110110 010100001110110
print(all(registro_g(m, g) == registro_h(m, h, 15)
for m in product([0, 1], repeat=5)))
# True
Dos detalles muerden. El reversed(u) es el orden de entrada: el registro recibe primero. Y en registro_h el bit nuevo se escribe en v antes de pasar al j siguiente, porque el próximo lo usa: calcular todas las paridades a partir del mensaje solo, sin realimentar, sale mal desde .
El orden de entrada. Si el mensaje entra desde , el registro calcula el resto del mensaje dado vuelta, mientras la derivación directa sigue mandando el mensaje en su orden. Medido sobre todos los mensajes, la palabra sale mal en 12 de los 16 del (7,4) y en 24 de los 32 del (15,5); los que se salvan son los capicúa. Y ninguna de las palabras mal armadas es del código: el error no pasa en silencio, pero el receptor ve un síndrome no nulo en tres de cada cuatro mensajes, sin un solo error del canal.
El tiempo. El registro entrega un bit por ciclo, ciclos por palabra, y durante los de descarga tiene la compuerta cerrada y no acepta mensaje nuevo. Ahorra hardware a cambio de serializar: el producto por , con sus coeficientes, calcula todos los bits de paridad a la vez.
Lo que sigue sirviendo. El mismo divisor, alimentado con la palabra recibida, deja en el registro el resto de dividirla por , que es el síndrome del código cíclico. Sin entrada, realimentándose solo, es un LFSR. Y acortar el código no cambia el circuito: es la base del CRC.