CODIFICA A BLOCCHI E CODICI LINEARI Abbiamo visto come nella codifica ciascuna delle 2k k-ple di informazione viene trasformata in una delle 2n n-ple (nk), dove k=n corrisponde all’assenza di codifica. Definiamo quindi in questo modo la codifica a blocchi: Ad ogni blocco di k bit di informazione si associa una parola di codice di lunghezza n. Gli M blocchi di informazione saranno M=2k. Le possibili parole di codice saranno 2n>2k. Scegliere un codice significa selezionare 2k tra le 2n possibili parole codice. La scelta va eseguita in modo che le parole codice siano quanto più differenti tra di lor,o cioè quanto più i segnali si siano distanti tra di loro, utilizzando come misura la distanza di Hamming dH. All’aumentare di k il numero delle parole di codice M=2k cresce esponenzialmente, e così di conseguenza anche la complessità del codificatore e del decodificatore. E’ necessario allora imporre al codice a blocchi un’ulteriore proprietà al fine di ottenere una complessità accettabile. Si vedrà che la proprietà di linearità porterà a diminuire la complessità algoritmica della codecodifica. Supponiamo infatti di avere un codice (n,k), con R=k/n. Prendiamo come indice di complessità dell’algoritmo di decodifica il numero di binit da memorizzare per implementare l’algoritmo: in generale è richiesta la memorizzazione della tabella relativa alla corrispondenza biunivoca fra parole di sorgente e parole di codice : 2k righe per k+n colonne, ossia n(R+1)2nR celle di memoria binarie. La capacità di memoria necessaria cresce quindi esponenzialmente con n, e diviene ben presto impraticabile, anche per n intorno al centinaio. D’altra parte, se si vuole arrivare vicini al limite indicato dal secondo teorema di Shannon è necessario arrivare a valori elevati di n. Occorre quindi trovare classi di codici che abbiano una struttura tale da permettere operazioni di codecodifica con algoritmi la cui complessità che non cresca esponenzialmente con n. Sarebbe opportuno che la parola di codice c da associare alla parola di ingresso a, anzichè riferirsi ad una tabella, fosse calcolabile come c=F(a), con F funzione vettoriale ad n componenti della variabile a a k componenti. Questoo richiede di definire un particolare tipo di algebra non ordinaria, in cui ci riferisce ad un insieme finito A di valori che rappresentano le cifre dell’alfabeto impiegato. Questa algebra viene definita su un Campo di Galois contenente A elementi, ed indicata come GF(A). In un campo GF(A) risultano definite due operazioni, somma e prodotto. Queste operano su due operandi costituiti da elementi del campo, e forniscono un risultato che è ancora un elemento del campo. Consideriamo il campo GF(2), particolarmente semplice perché i due elementi che lo compongono possono essere indicati con 0 e 1, e le operzioni di somma e prodotto si identificano con somma modulo 2 (operazione logica XOR o OR esclusivo) e prodotto algebrico ordinario. L'operatore XOR, detto anche EX-OR, OR esclusivo o somma modulo 2, restituisce 1 se e solo se la somma degli operandi uguali ad 1 è dispari, mentre restituisce 0 in tutti gli altri casi. A B A XOR B 0 0 0 0 1 1 1 0 1 1 1 0 Definiamo ora la funzione F citata. Il modo più semplice è quello di considerarla lineare, ottenendo così l’importante classe dei codici lineari. Ciascuno dei simboli della parola di codice c risulta una combinazione lineare dei simboli della parola di ingresso a, con opportuni coefficienti che definiscono il codice. Anche i coefficienti devono essere elementi del campo GF(2). Una generica funzione vettoriale lineare c=F(a) può esprimersi utilizzando la notazione matriciale c= a G dove c ed a sono vettori riga e G una matrice di k righe ed n colonne, costituita da elementi appartenenti a GF(2). La matrice G è detta matrice generatrice del codice. Il numero di elementi di memoria necessari nel codificatore si riduce in questo caso al numero di elementi della matrice G , che ha il valore kn=n2R e quindi cresce con n2 . Questa è una significativa riduzione di complessità: ad esempio, per n=100 e k=80, la matrice G è costituita da 8000 elementi, mentre la tabella di corrispondenza biunivoca avrebbe circa 1030 elementi. La decodifica prevede l’impiego di una matrice analoga a G, ma richiede una crescita esponenziale della complessità con esponente (1-R)n , anziché Rn, permettendo di utilizzare codici con elevato valore di n purchè R abbia valori elevati, ossia prossimi all’unità. CODIFICA E DECODIFICA DI CODICI LINEARI BINARI Prendiamo in considerazione codici binari (cioè definiti in GF(2)). Indichiamo con dm la distanza del codice, e indichiamo con “peso” di una parola il numero di cifre “1” che vi appaiono. Si consideri un ccodice lineare binario (n,k) definito dalla matrice generatrice i cui elementi gij sono elementi binari (0,1): g 00 g 10 G = ... g k -10 g 01 g 11 ... g k -11 g 0n -1 ... g 1n 1 ... ... ... g k -1n -1 ... Per un codice lineare generico valgono le seguenti proprietà: Proprietà 1: In un codice lineare esiste sempre la parola di codice nulla c=0 (0 è un vettore riga con componenti tutte nulle) Dim.: Ponendo a=0 si ottiene c=aG=0 per ogni G. Proprietà 2: Le righe della matrice G sono parole di codice. Dim.: Quando a sia formata da n-1 “0” e un solo “1” in posizione i-esima, il prodotto c=aG risulta uguale alla i-esima riga di G. Proprietà 3: La somma di due parola di codice c’ e c’’ è ancora una parola dello stesso codice. Proprietà 4: La distanza dm di un codice lineare è uguale al peso pm della parola di codice che ha peso minimo, escludendo quella di peso nullo. CODICI SISTEMATICI I codici sistematici sono un’important classe di codici lineari definiti dalla proprietà di avere nella parola codificata i primi k binit identicamente uguali ai k binit della corrispondente parola di ingresso: [c0,c1,…,ck-1]=[ a0,a1,…,ak-1]=a Quindi le prime k colonne della matrice G costituiscono una matrice identità Ik, e le ultime n-k colonne, che formano una matrice P di dimensioni k(n-k), definiscono quindi completamente il codice: 1 0 G = [Ik P] = ... 0 g1k g 2k ... g1k-1 g 2k1 ... 0 ... 1 g k -1k g k -1k 1 0 ... 0 1 ... 0 ... ... ... g1n-1 g 2 n 1 ... ... g k -1n-1 ... ... ... Questa classe di codici è importante perché si può dimostrare che dato un qualsiasi codice (n,k) non sistematico, è possibile trovare un codice sistematico con uguali valori di n e k che ha le stesse prestazioni del codice dato, rispetto alla capacità di correzione di errori. Consideriamo ora il problema della decodifica nel caso in cui si voglia effettuare una correzione d’errore. Supponiamo di voler realizzare la decodifica in accordo al criterio della massima verosimiglianza (Maximum Likelihood Decision, MLD), ossia tale per cui ricevuta una parola r si ipotizza come trasmessa la parola c* per la quale fra tutte le parole di codice è massima la probabilità di transizione P(r|c*) . Allora supponiamo che sia stata ricevuta una particolare parola r : la probabilità P(r|c) che r sia stata ottenuta per effetto della trasmissione di una parola di codice c equivale alla probabilità che, nella trasmissione di n simboli, si verifichino e errori, disposti nelle posizioni in cui r differisce da c. Poniamo come regola di decisione la seguente regola: Ricevuta la parola r, ipotizzare come trasmessa quella parola di codice c* che ha la distanza minima da r , dove indichiamo come distanza minima il parametro dm (0 dm n) . La decodifica avviene in due passi successivi: 1. determinazione della parola di codice ĉ che ha distanza minima dalla parola ricevuta r 2. determinazione della parola di informazione â associata a ĉ nella corrispondenza biunivoca che definisce il codice. 3. In un codice lineare sistematico questo passo 2 è banale perché consiste nel prelevare i primi k binit della parola ĉ . Per definire il primo passo occorre definire la matrice H (matrice di controllo o check matrix) di dimensioni n(n-k) ed espressa come P H= I n k (9.0) dove In-k è la matrice identità di dimensioni (n-k). La matrice H viene impiegata nella decodifica ma permette anche di acquisire informazioni sulla struttura del codice. Infatti si consideri il prodotto s=xH (9.1) dove x è una sequenza arbitraria di n binit. Il risultato del prodotto è un vettore riga s con n-k componenti binarie, detto sindrome. Si vede subito che s=0 se e soltanto che x coincide con una parola di codice c, ossia xH=0 (9.2) costituisce condizione necessaria e sufficiente affinchè x sia parola di codice. Questa condizione è utile per dedurre la forma della matrice H per costruire un codice di assegnata distanza dm. Vale infatti la Proprietà 5: La distanza dm di un codice lineare avente matrice di controllo H è uguale al numero minimo di righe di H che, scelte in modo arbitrario e sommate simbolo a simbolo, diano luogo ad una sequenza di n-k binit costituita da tutti 0. La dimostrazione segue dalla (9.2) e dalla Proprietà 4. In particolare, sempre dalla Proprietà 4 discende che se una generica riga di H è costituita da tutti 0, si ha chiaramente dm=1: condizione quindi da evitare per evitare che il codice sia inutile. Se nessuna riga di H è nulla, ma due righe sono uguali fra loro, allora la somma delle due righe è nulla, e quindi dm=2. Se le righe di H sono tutte diverse e non nulle allora occorrono almeno tre righe affinchè la loro somma sia nulla, e si ha dm 3. Dunque per avere un codice con distanza minima fra parole non inferiore a 3 si riesce ad avere una semplice scelta della matrice P : infatti poiché le ultime k righe della matrice H sono tutte e sole le sequenze di k binit contenenti un solo 1, si potrà avere dm 3 soltanto se le righe di P sono tutte diverse e ciascuna contiene almeno due 1. Nel caso di questi codici lineari la decodifica si effettua osservando la parola r ricevuta all’uscita dal canale e calcolando la sindrome s = r H. Se non si sono verificati errori di trasmissione la sindrome è nulla, in quanto r coincide con la parola di codice trasmessa. La rilevazione d’errore si effettua quindi verificando se la sindrome è uguale o diversa da zero. Tuttavia s = 0 non garantisce la certezza di trasmissione corretta, perché può verificarsi un errore che trasformi una parola in un’altra di sindrome nulla. Se chiamiamo e la configurazione d’errore, si ha s=rH=(c+e)H=cH+eH=eH ossia la sindrome è funzione solo della configurazione d’errore e contenente un numero e di simboli errati. Il decodificatore determinerà una stima ê della configurazione d’errore in base al valore di s calcolato dalla (9.1). Si calcolerà allora ĉ come differenza (in GF(2)) fra la configurazione d’errore ê e la parola ricevuta r: ĉ = r + ê . Ora, la parola ĉ H=( r + ĉ è una parola di codice, infatti ê )H = rH + ê H = s + s = 0 . Ma se scegliamo fra le 2k configurazioni d’errore possibili (cioè fra quelle che possono aver dato luogo alla sindrome s) quella di peso minimo, ĉ risulta anche la parola di codice che ha distanza minima da r. Questo realizza una decisione a massima verosimiglianza come definito più sopra. Per quanto riguarda la complessità della decodifica, il numero di elementi binari di H è n(nk)=n2(1-R) , quindi per R costante la complessità cresce come n2, come per G. Ma nel decodificatore dobbiamo anche memorizzare la tabella che deinisce la corrispondenza biunivoca fra le sindromi e le configurazioni di errore a peso minimo: tabella che ha 2n-k righe (numero delle sindromi distinte) per 2n-k colonne (somma della lunghezza della sindrome e della configurazione d’errore). Possiede quindi n(2-R)2n(1-R) elementi binari. La tabella cresce quindi, come si era detto in modo esponenziale ma con esponente (1-R)n. I CODICI DI HAMMING: CLASSE DI CORRETTORI D’ERRORE SINGOLO. I vincoli cui deve soddisfare la matrice H per garantire una distanza del codice dm3 permettono di definire una classe di codici sistematici con distanza dm=3 . Assegnando infatti in modo arbitrario il valore della differenza n-k , è possibile determinare dalla (9.0) una matrice H in modo che contenga tutte e sole le 2n-k-1 combinazioni distinte (esclusa quella formata da tutti 0) formate da n-k simboli, ordinate in modo arbitrario salvo che quelle che contengono un solo 1 che devono far parte della matrice In-k. Una volta assegnata in questo modo H, risulta assegnata la matrice P e quindi il relativo codice sistematico. Un codice di questo tipo ha distanza dm=3 per le ragioni già esposte e soddisfa la relazione n=2n-k-1. Per ogni valore n-k>1 esisterà un insieme di codici (di prestazioni equivalenti) con dm=3, ottenibili da tutte le permutazioni delle prime k righe di H, determinati come sopra. La classe di codici così ottenuta è detta di codici di Hamming. Essi hanno la proprietà, facilmente verificabile costruendo le tabelle di decodifica, di correggere tutte e sole le configurazioni di un solo errore. Infatti queste sono in numero di n e risultano biunivocamente associate alle 2n-k-1 sindromi non nulle. I codici aventi distanza dm=2t+1 in grado di correggere tutte e sole le configurazioni di non più di t errori (t1) vengono detti codici perfetti. Infatti si può dimostrare che un codice avente distanza d m=2t+1 che corregga non solo tutte le configurazioni con più di t errori ma anche quelle con più di t errori non contribuisce significativamente alla riduzione della probabilità di decodifica errata rispetto ad un codeice perfetto che corregga solo quelle con al più t errori. PROPRIETA’ DEL CODICE DI HAMMING Il codice di Hamming (1950) è un particolare codice a correzione d’errore che: • Consente di correggere 1 singolo errore • Ha un numero di bit di controllo pari al limite teorico inferiore (m+1≤2r-r) • Funziona con qualunque dimensione del messaggio m • I bit della parola di codice vengono numerati da sinistra verso destra cominciando con l’indice 1 • I bit di controllo sono quelli aventi come indice una potenza di due (1, 2, 4,8, 16, ... ) • I bit del messaggio sono tutti gli altri bit della parola di codice, nell’ordine il bit di controllo con indice 2k è il bit di parità dei bit del messaggio i cui indici hanno il termine 2k nella loro scomposizione in somma di potenze di due • In ricezione, ciascun bit di controllo viene ricalcolato: – Se tutti i valori dei bit di controllo sono corretti, la parola di codice viene accettata – Se alcuni bit di controllo hanno valori non corretti, l’indice del bit in cui si è verificato l’errore è dato dalla somma degli indici dei bit di controllo con valore sbagliato.