El decodificador está compuesto por dos bloques A Posteriori Probability (APP) que recibe cada uno los bits sistemáticos y los bits de paridad del demodulador previamente pre-procesados para su decodificación. Esta etapa se puede apreciar en el modelo de Simulink en la figura 4.4.
Figura 4.4 Decodificador Iterativo en Simulink
Aquí se puede apreciar que los bits entrantes al primer decodificador son R0 y R1, que
corresponden a los vectores xk y y1 . Éstos son procesados para calcular la información k extrínseca denotada por L(u) y enviada a un bloque entrelazador para su reordenamiento para que sirva como información para mejorar la confiabilidad de la decisión. Los bits sistemáticos son reordenados en una etapa anterior por un bloque de entrelazado representados por R’ y, junto con los bits de paridad R2 del segundo codificador convolucional, correspondientes al vector y2 , k
son enviados al siguiente bloque decodificador APP. Este decodificador está retroalimentado a través de su salida L(u) y es reordenado a través de un desentrelazador e introducidos nuevamente al primer decodificador APP como información a priori mejorando la confiabilidad en la decisión nuevamente. Después de un número determinado de iteraciones, el decodificador corrige los errores ocasionados por el canal y su rendimiento cada vez es mejor. Al final se toma una decisión dura para obtener la información decodificada.
En el programa en C++ la información a priori está representada por el vector zk y los bloques decodificadores por la función logMap( ). El proceso iterativo está dado por un ciclo for con el número de máximo de iteraciones especificadas por la constante MAX_ITER de la cabecera
parametros.h. El bloque decodificador se muestra a continuación:
for(iter = 1; iter <= numIteraciones; iter++) {
logMap(trellisUMTS, xk, yk1, zk, frameSize, Le1); for(i=0; i<frameSize; i++)
{
Le1Interleaved[index[i]] = Le1[i]; xkInterleaved[index[i]] = xk[i]; }
logMap(trellisUMTS, xkInterleaved, yk2, Le1Interleaved, frameSize, Le2); for(i = 0; i < frameSize; i++)
{
zk[i] = Le2[index[i]]; }
for(i = 0; i < frameSize; i++) dec[i] = xk[i] + Le1[i] + zk[i]; }
En el listado anterior se puede apreciar el ciclo para generar las iteraciones. La primera llamada a la función logMap( ) recibe como argumentos los vectores xk, y1 y k zk donde zk es la información a priori inicializada en ‘0’ para la primera iteración. Esta función deja una referencia al vector con la información extrínseca Le1 correspondiente al primer decodificador que k posteriormente es reordenado junto con los datos xk para ser procesados posteriormente por el segundo decodificador logMap( ). Este decodificador, al igual que el primero, procesa los vectores reordenados xk y Le1 como argumentos de entrada y deja una referencia a su propia k información extrínseca Le2 para ser procesada posteriormente por la decisión dura o ser k retroalimentada al primer decodificador asignando la referencia a zk. La información extrínseca de cada decodificador es sumada junto con los bits xk para mejorar su confiabilidad.
4.4.1 Función logMap( )
La función logMap( ) realiza todo el procesamiento para el cálculo de las métricas de rama y las recursiones hacia adelante y hacia atrás, ayudado por otras funciones mencionadas en el capítulo 3. Se puede encontrar en la cabecera dsp.h del programa en C++ junto con todas las funciones utilizadas para realizar su tarea.
El decodificador inicia con las métricas para el estado ‘0’ alpha
[ ]
0 =1 y beta[ ]
0 =1. Para todos los estados restantes del trellis UMTS los valores son alpha[ ]
i =−1000 y beta[ ]
i =−1000, sustituyendo los valores −∞ como se muestra en la siguiente lista de instrucciones:for(i = 0; i < trellis.numStates; i++) { alpha[i] = -1000; beta[i] = -1000; } alpha[0] = beta[0] = 1;
El sumador que se aprecia a la entrada del primer decodificador de la figura 3.21, suma los vectores xk y zk siendo zk inicializada a cero. Esto se puede ver a continuación:
for(i = 0; i < frameSize; i++) xk[i] = xk[i] + zk[i];
Posteriormente se calcularán las métricas de rama gamma para todos los estados de cada bit en el tiempo k. Esto se logra con las llamadas a las funciones getUkCero( ), getVkCero( ) que regresan los bits “esperados” del diagrama de trellis cuando se introduce el bit ‘0’ y las funciones
getUkUno( ) y getVkUno( ) que regresan los bits “esperados” del diagrama de trellis cuando se introduce el bit ‘1’. Estas funciones presentan los bits correspondientes a uk en la ecuación 3.19. (Ver anexo A).
Una vez calculadas las métricas gamma para el primer tiempo k, se procesarán las alpha’s en recursión hacia adelante a través de la función ACSAlphas( ), que tiene como objetivo obtener la
alpha siguiente utilizando la función MAX*. A continuación se muestran las funciones mencionadas:
double ACSAlphas (double alpha1, double alpha2, double gammaMov1, double gammaMov2) {
double AG1 = alpha1 + gammaMov1; double AG2 = alpha2 + gammaMov2; return MAXAsteriks (AG1, AG2); }
La función ACSAlphas( ) toma como argumentos de entrada las alpha’s anteriores con sus respectivas métricas de rama gamma y los envía a la función MAXAsteriks( ) que devuelve el número máximo con un ajuste. En la siguiente lista se muestra esta función:
double MAXAsteriks (double x, double y) {
return max(x,y) + log(1 + exp( - abs(x-y) ) ); }
Las dos funciones anteriores, ACSAlphas( ) y MAXAsteriks( ), simulan la arquitectura del ACS de la figura 3.26.
Dentro del mismo ciclo de recursión hacia adelante, se realiza el ciclo de recursión hacia atrás para el cálculo de las betas’s. Debido a que el proceso es el mismo que el de las alpha’s, es necesario calcular las gamma’s de todas las ramas hacia atrás obteniendo las beta’s anteriores hasta llegar al tiempo k+1 de donde se encuentre posicionado alpha. Para realizar esto se utiliza de igual forma, el módulo Adiciona-Compara-Selecciona (ACS) como se muestra a continuación:
double ACSBetas (double betaSiguiente1, double betaSiguiente2, double gammaMov0, double gammaMov1)
{
double BG1 = betaSiguiente1 + gammaMov0; double BG2 = betaSiguiente2 + gammaMov1; return MAXAsteriks (BG1, BG2);
}
Una vez calculado y guardado en un vector las alpha’s correspondientes al tiempo k, en otro vector las gamma’s y teniendo las beta’s en el tiempo k+1, se procede a realizar el cálculo del LLR para ese tiempo k de la siguiente forma:
for(estado = 0; estado < trellis.numStates; estado++) {
llrCero[estado] = alpha[estado] + gamma[estado*2] + beta[trellis.nextStates[estado][0]] ; llrUno[estado] = alpha[estado] + gamma[estado*2+1] + beta[trellis.nextStates[estado][1]] ; }
for(estado = 0; estado < trellis.numStates; estado++) { LLRCero = arbolMAXAsteriks(llrCero);
LLRUno = arbolMAXAsteriks(llrUno); Le[kAlfa] = (LLRUno - LLRCero)/8; }
Aquí se puede apreciar la salida Le que representa la información extrínseca hacia el siguiente decodificador. Además, se muestra la función arbolMAXAsteriks( ) utilizado en el proceso del cálculo del LLR mostrado en la figura 3.30. A continuación se presenta esta función:
double arbolMAXAsteriks (double *llr) {
double M1 = MAXAsteriks(llr[0], llr[1]); double M2 = MAXAsteriks(llr[2], llr[3]);
double M3 = MAXAsteriks(llr[4], llr[5]); double M4 = MAXAsteriks(llr[6], llr[7]); double MM1 = MAXAsteriks(M1, M2); double MM2 = MAXAsteriks(M3, M4); return MAXAsteriks(MM1, MM2); }
Como se puede apreciar, la función se fija al número de estados del diagrama de trellis de UMTS por razones de eficiencia en el procesamiento ya que toma por pares los 8 estados correspondientes a los bits de entrada ‘0’ y ‘1’ con llamadas a la función MAXAsteriks( ).
Finalmente, al término de las iteraciones especificadas, se toma una decisión utilizando el umbral ‘0’ con los bits decodificados y dejando una referencia a éste vector como se muestra en la siguiente lista de instrucciones:
for(i = 0; i < frameSize; i++) { if(dec[i] < 0) turboDecode[i] = 0; else turboDecode[i] = 1; }