package rtty; public class Turbo_decoder { int _max_iterations = 10; boolean last_success = false; int last_fixed = 0; private static int[][] interleaver_parameters = new int[][]{{40,3,10}, {48,7,12}, {56,19,42}, {64,7,16}, {72,7,18}, {80,11,20}, {88,5,22}, {96,11,24}, {104,7,26}, {112,41,84}, {120,103,90}, {128,15,32}, {136,9,34}, {144,17,108}, {152,9,38}, {160,21,120}, {168,101,84}, {176,21,44}, {184,57,46}, {192,23,48}, {200,13,50}, {208,27,52}, {216,11,36}, {224,27,56}, {232,85,58}, {240,29,60}, {248,33,62}, {256,15,32}, {264,17,198}, {272,33,68}, {280,103,210}, {288,19,36}, {296,19,74}, {304,37,76}, {312,19,78}, {320,21,120}, {328,21,82}, {336,115,84}, {344,193,86}, {352,21,44}, {360,133,90}, {368,81,46}, {376,45,94}, {384,23,48}, {392,243,98}, {400,151,40}, {408,155,102}, {416,25,52}, {424,51,106}, {432,47,72}, {440,91,110}, {448,29,168}, {456,29,114}, {464,247,58}, {472,29,118}, {480,89,180}, {488,91,122}, {496,157,62}, {504,55,84}, {512,31,64}, {528,17,66}, {544,35,68}, {560,227,420}, {576,65,96}, {592,19,74}, {608,37,76}, {624,41,234}, {640,39,80}, {656,185,82}, {672,43,252}, {688,21,86}, {704,155,44}, {720,79,120}, {736,139,92}, {752,23,94}, {768,217,48}, {784,25,98}, {800,17,80}, {816,127,102}, {832,25,52}, {848,239,106}, {864,17,48}, {880,137,110}, {896,215,112}, {912,29,114}, {928,15,58}, {944,147,118}, {960,29,60}, {976,59,122}, {992,65,124}, {1008,55,84}, {1024,31,64}, {1056,17,66}, {1088,171,204}, {1120,67,140}, {1152,35,72}, {1184,19,74}, {1216,39,76}, {1248,19,78}, {1280,199,240}, {1312,21,82}, {1344,211,252}, {1376,21,86}, {1408,43,88}, {1440,149,60}, {1472,45,92}, {1504,49,846}, {1536,71,48}, {1568,13,28}, {1600,17,80}, {1632,25,102}, {1664,183,104}, {1696,55,954}, {1728,127,96}, {1760,27,110}, {1792,29,112}, {1824,29,114}, {1856,57,116}, {1888,45,354}, {1920,31,120}, {1952,59,610}, {1984,185,124}, {2016,113,420}, {2048,31,64}, {2112,17,66}, {2176,171,136}, {2240,209,420}, {2304,253,216}, {2368,367,444}, {2432,265,456}, {2496,181,468}, {2560,39,80}, {2624,27,164}, {2688,127,504}, {2752,143,172}, {2816,43,88}, {2880,29,300}, {2944,45,62}, {3008,157,188}, {3072,47,96}, {3136,13,28}, {3200,111,240}, {3264,443,204}, {3328,51,104}, {3392,51,212}, {3456,451,192}, {3520,257,220}, {3584,57,336}, {3648,313,228}, {3712,271,232}, {3776,179,236}, {3840,331,120}, {3904,363,244}, {3968,375,248}, {4032,127,168}, {4096,31,64}, {4160,33,130}, {4224,43,264}, {4288,33,134}, {4352,477,408}, {4416,35,138}, {4480,233,280}, {4544,357,142}, {4608,337,480}, {4672,37,146}, {4736,71,444}, {4800,71,120}, {4864,37,152}, {4928,39,462}, {4992,127,234}, {5056,39,158}, {5120,39,80}, {5184,31,96}, {5248,113,902}, {5312,41,166}, {5376,251,336}, {5440,43,170}, {5504,21,86}, {5568,43,174}, {5632,45,176}, {5696,45,178}, {5760,161,120}, {5824,89,182}, {5888,323,184}, {5952,47,186}, {6016,23,94}, {6080,47,190}, {6144,263,480}}; private int[] interleaver = new int[]{0}; public static boolean check_checksum( boolean [] systematic) { int crc = 0xFFFF; int i = 0; if (systematic.length < 16) return false; while (i < systematic.length-16) { byte in = 0; int mask = 0x80; while (i < systematic.length-16 && mask != 0) { if (systematic[i]) in |= mask; mask >>= 1; i++; } crc = crc_xmodem_update(crc,in); } int in_crc = 0; int mask = 0x8000; for (i = systematic.length-16; i < systematic.length; i++) { if (systematic[i]) in_crc |= mask; mask >>= 1; } return ((in_crc & 0xFFFF) == (crc & 0xFFFF)); } public boolean[] decode( double[] d0, double[] d1, double[] d2, boolean checksum, boolean termination) { if (d0.length != d1.length) throw new IllegalArgumentException("Check Input Array Lengths"); if (d0.length != d2.length) throw new IllegalArgumentException("Check Input Array Lengths"); int bits,decoder_bits; last_success = false; if (termination){ bits = d0.length - 4; decoder_bits = bits + 3; }else{ bits = d0.length; decoder_bits = bits; } if (interleaver.length != bits) interleaver = get_interleaver(bits,termination); double[] decoder1_out = null; double[] decoder2_out = new double[decoder_bits]; //termination bits double[] xk = new double[3]; double[] zk = new double[3]; double[] zpk = new double[3]; double[] xpk = new double[3]; //termination demultiplexing if (termination) { //5.1.3.2.2 int k = bits; xk[0] = d0[k]; zk[1] = d0[k+1]; xpk[0] = d0[k+2]; zpk[1] = d0[k+3]; zk[0] = d1[k]; xk[2] = d1[k+1]; zpk[0] = d1[k+2]; xpk[2] = d1[k+3]; xk[1] = d2[k]; zk[2] = d2[k+1]; xpk[1] = d2[k+2]; zpk[2] = d2[k+3]; } //signals input to the decoder double[] llrs_sys = new double[decoder_bits]; double[] llrs_dec1 = new double[decoder_bits]; double[] llrs_dec2 = new double[decoder_bits]; boolean[] out = new boolean[bits]; if (termination){ llrs_dec1[bits] = zk[0]; llrs_dec1[bits+1] = zk[1]; llrs_dec1[bits+2] = zk[2]; llrs_dec2[bits] = zpk[0]; llrs_dec2[bits+1] = zpk[1]; llrs_dec2[bits+2] = zpk[2]; } for (int i = 0; i < bits; i++) { llrs_sys[i] = d0[i]; llrs_dec1[i] = d1[i]; llrs_dec2[i] = d2[i]; } for (int i = 0; i < _max_iterations; i++) { if (termination){ llrs_sys[bits] = xk[0]; llrs_sys[bits+1] = xk[1]; llrs_sys[bits+2] = xk[2]; decoder2_out[bits] = 0; decoder2_out[bits+1] = 0; decoder2_out[bits+2] = 0; } decoder1_out = bcjr ( decoder2_out, llrs_sys, llrs_dec1, false, termination); if (termination){ llrs_sys[bits] = xpk[0]; llrs_sys[bits+1] = xpk[1]; llrs_sys[bits+2] = xpk[2]; decoder1_out[bits] = 0; decoder1_out[bits+1] = 0; decoder1_out[bits+2] = 0; } decoder2_out = bcjr ( decoder1_out, llrs_sys, llrs_dec2, true, termination); //check checksum if (checksum) { last_fixed = 0; for (int j = 0; j < bits; j++){ out[j] = decoder1_out[j]+decoder2_out[j]+llrs_sys[j]>0 ? true:false; if (out[j] != llrs_sys[j]>0) last_fixed++; } if (Turbo_decoder.check_checksum(out)) { last_success = true; return out; } } } return out; } private double[] bcjr (double[] uncoded1, double[] uncoded2, double[] coded, boolean interleave, boolean termination) { //inter = 0 - no interleaver //inter = 1 - interleave on uncoded side if (uncoded1.length != coded.length) throw new IllegalArgumentException("Check Input Array Lengths"); if (uncoded2.length != coded.length) throw new IllegalArgumentException("Check Input Array Lengths"); int bits = uncoded1.length; double[][] alphas = new double[bits][8]; double[][] betas = new double[bits][8]; double[] deltas = new double[16]; double[] out = new double[bits]; /////////// forward recursion ///////////// //initialise first state alphas[0][0] = 0; for (int i = 1; i < 8; i++) alphas[0][i] = -9000; //do the rest double un,en,bo,no,o0,o1; no = 0; for (int i = 1; i < bits; i++) { if (interleave) un = uncoded1[interleaver[i-1]]+uncoded2[interleaver[i-1]]; else un = uncoded1[i-1]+uncoded2[i-1]; en = coded[i-1]; bo = un+en; //inverse bodge //bo = un; //un = en; //en = bo; //bo = 0; //no = en+un; ///// alphas[i][0] = Math.max(alphas[i-1][0] + no , alphas[i-1][1] + bo ); alphas[i][1] = Math.max(alphas[i-1][2] + un , alphas[i-1][3] + en ); alphas[i][2] = Math.max(alphas[i-1][4] + en , alphas[i-1][5] + un ); alphas[i][3] = Math.max(alphas[i-1][6] + bo , alphas[i-1][7] + no ); alphas[i][4] = Math.max(alphas[i-1][0] + bo , alphas[i-1][1] + no ); alphas[i][5] = Math.max(alphas[i-1][2] + en , alphas[i-1][3] + un ); alphas[i][6] = Math.max(alphas[i-1][4] + un , alphas[i-1][5] + en ); alphas[i][7] = Math.max(alphas[i-1][6] + no , alphas[i-1][7] + bo ); } /////////// backward recursion ///////////// //initialise last state for (int i = 0; i < 8; i++) betas[bits-1][i] = -9000; if (termination) betas[bits-1][0] = 0; //do the rest for (int i = bits-2; i >= 0; i--) { if (interleave) un = uncoded1[interleaver[i+1]]+uncoded2[interleaver[i+1]]; else un = uncoded1[i+1]+uncoded2[i+1]; en = coded[i+1]; bo = un+en; //inverse bodge //bo = un; //un = en; //en = bo; //bo = 0; //no = en+un; ///// betas[i][0] = Math.max(betas[i+1][0] + no , betas[i+1][4] + bo ); betas[i][1] = Math.max(betas[i+1][0] + bo , betas[i+1][4] + no ); betas[i][2] = Math.max(betas[i+1][1] + un , betas[i+1][5] + en ); betas[i][3] = Math.max(betas[i+1][1] + en , betas[i+1][5] + un ); betas[i][4] = Math.max(betas[i+1][2] + en , betas[i+1][6] + un ); betas[i][5] = Math.max(betas[i+1][2] + un , betas[i+1][6] + en ); betas[i][6] = Math.max(betas[i+1][3] + bo , betas[i+1][7] + no ); betas[i][7] = Math.max(betas[i+1][3] + no , betas[i+1][7] + bo ); } /////////// delta and output ///////////// //output the LLR for (int i = 0; i < bits; i++) { if (interleave) un = uncoded1[interleaver[i]]+uncoded2[interleaver[i]]; else un = uncoded1[i]+uncoded2[i]; en = coded[i]; bo = un+en; //inverse bodge //bo = un; //un = en; //en = bo; //bo = 0; //no = en+un; ///// deltas[0] = alphas[i][0] + betas[i][0] + no; //0 deltas[1] = alphas[i][1] + betas[i][0] + bo; //1 deltas[2] = alphas[i][2] + betas[i][1] + un; //1 deltas[3] = alphas[i][3] + betas[i][1] + en; //0 deltas[4] = alphas[i][4] + betas[i][2] + en; //0 deltas[5] = alphas[i][5] + betas[i][2] + un; //1 deltas[6] = alphas[i][6] + betas[i][3] + bo; //1 deltas[7] = alphas[i][7] + betas[i][3] + no; //0 deltas[8] = alphas[i][0] + betas[i][4] + bo; //1 deltas[9] = alphas[i][1] + betas[i][4] + no; //0 deltas[10] = alphas[i][2] + betas[i][5] + en; //0 deltas[11] = alphas[i][3] + betas[i][5] + un; //1 deltas[12] = alphas[i][4] + betas[i][6] + un; //1 deltas[13] = alphas[i][5] + betas[i][6] + en; //0 deltas[14] = alphas[i][6] + betas[i][7] + no; //0 deltas[15] = alphas[i][7] + betas[i][7] + bo; //1 o0 = Math.max( Math.max( deltas[0], deltas[3] ) , Math.max( deltas[4] , deltas[7] )); o0 = Math.max(o0, Math.max( Math.max( deltas[9], deltas[10]) , Math.max( deltas[13], deltas[14]))); o1 = Math.max( Math.max( deltas[1], deltas[2] ) , Math.max( deltas[5] , deltas[6] )); o1 = Math.max(o1, Math.max( Math.max( deltas[8], deltas[11]) , Math.max( deltas[12], deltas[15]))); if (interleave) out[interleaver[i]] = o1-o0-un; else out[i] = o1-o0-un; } return out; } public static int[] get_interleaver(int len) { int[] interleaver_o = new int[len]; int f1,f2; int index = find_interleaver_param_index(len); if (index < 0) throw new IllegalArgumentException("Interleaver Length not Supported"); f1 = interleaver_parameters[index][1]; f2 = interleaver_parameters[index][2]; for (int i = 0; i < len; i++) interleaver_o[i] = (f1*i+f2*i*i)%len; return interleaver_o; } public static int[] get_interleaver(int len, boolean termination) { int[] interleaver_o; if (termination) interleaver_o = new int[len+3]; else interleaver_o = new int[len]; int f1,f2; int index = find_interleaver_param_index(len); if (index < 0) throw new IllegalArgumentException("Interleaver Length not Supported"); f1 = interleaver_parameters[index][1]; f2 = interleaver_parameters[index][2]; for (int i = 0; i < len; i++) interleaver_o[i] = (f1*i+f2*i*i)%len; if (termination){ interleaver_o[len] = len; interleaver_o[len+1] = len+1; interleaver_o[len+2] = len+2; } return interleaver_o; } private static int find_interleaver_param_index(int len) { for (int i = 0; i < interleaver_parameters.length; i++) { if (interleaver_parameters[i][0] == len) return i; } return -1; } public static double[] systematic_subblock_deinterleave(double[] llrs_in) { int colTcSb = 32; int rowTcSb = (int) Math.ceil((double)llrs_in.length/colTcSb); int Nd = colTcSb*rowTcSb - llrs_in.length; boolean[] zeros = new boolean[llrs_in.length]; double[] output = new double[llrs_in.length]; int[] v0 = subBlockInterleaver1(zeros,colTcSb,rowTcSb,Nd); double[] llr_v0 = new double[v0.length]; //input all systematic, then alternate v1/v2 until enough bits inputted int k = 0; //total inputted int i = 0; //array index //input systematic first while (k < llrs_in.length && i < v0.length) { if (v0[i] >= 0) { llr_v0[i] = llrs_in[k]; k++; } i++; } output = subBlockDeInterleaver1(llr_v0,colTcSb,rowTcSb,Nd); return output; } public static double[][] output_rate_dematching(double[] llrs_in, int D) { int colTcSb = 32; int rowTcSb = (int) Math.ceil((double)D/colTcSb); int Nd = colTcSb*rowTcSb - D; boolean[] zeros = new boolean[D]; double[][] output = new double[D][3]; int[] v0 = subBlockInterleaver1(zeros,colTcSb,rowTcSb,Nd); int[] v2 = subBlockInterleaver2(zeros,colTcSb,rowTcSb,Nd); double[] llr_v0 = new double[v0.length]; double[] llr_v1 = new double[v0.length]; double[] llr_v2 = new double[v2.length]; //input all systematic, then alternate v1/v2 until enough bits inputted int k = 0; //total inputted int i = 0; //array index //input systematic first while (k < llrs_in.length && i < v0.length) { if (v0[i] >= 0) { llr_v0[i] = llrs_in[k]; k++; } i++; } //now parity i = 0; while (k < llrs_in.length && i < v0.length) { if (v0[i] >= 0) { llr_v1[i] = llrs_in[k]; k++; } if (v2[i] >= 0 && k < llrs_in.length) { llr_v2[i] = llrs_in[k]; k++; } i++; } output[0] = subBlockDeInterleaver1(llr_v0,colTcSb,rowTcSb,Nd); output[1] = subBlockDeInterleaver1(llr_v1,colTcSb,rowTcSb,Nd); output[2] = subBlockDeInterleaver2(llr_v2,colTcSb,rowTcSb,Nd); return output; } //As per TS 36.212 v10.0.0, Section 5.1.4.1. private static int[] subBlockInterleaver1(boolean[] in, int colTcSb, int rowTcSb, int Nd) { if (colTcSb*rowTcSb != in.length + Nd) throw new IllegalArgumentException("Check Input Array Lengths"); int[] colPermPat = new int[] {0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31}; int[][] y = new int[rowTcSb][colTcSb]; int[] out = new int[rowTcSb*colTcSb]; //fill matrix int k = 0; for (int i = 0; i < rowTcSb; i++){ for (int j = 0; j < colTcSb; j++){ if (Nd > 0) { y[i][j] = -2; //null Nd--; } else { y[i][j] = in[k]?1:0; k++; } } } k=0; for (int j = 0; j < colTcSb; j++){ for (int i = 0; i < rowTcSb; i++){ out[k] = y[i][colPermPat[j]]; k++; } } return out; } //As per TS 36.212 v10.0.0, Section 5.1.4.1. private static int[] subBlockInterleaver2(boolean[] in, int colTcSb, int rowTcSb, int Nd) { if (colTcSb*rowTcSb != in.length + Nd) throw new IllegalArgumentException("Check Input Array Lengths"); int[] colPermPat = new int[] {0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31}; int[] pi = new int[colTcSb*rowTcSb]; int[] out = new int[rowTcSb*colTcSb]; for (int i = 0; i < colTcSb*rowTcSb; i++) pi[i] = (colPermPat[(int)Math.floor(i/rowTcSb)] + colTcSb*(i%rowTcSb)+1) % (colTcSb*rowTcSb); for (int i = 0; i < colTcSb*rowTcSb; i++) { if (pi[i] < Nd) out[i] = -2; //null else out[i] = in[pi[i]-Nd]?1:0; } return out; } //As per TS 36.212 v10.0.0, Section 5.1.4.1. private static double[] subBlockDeInterleaver1(double[] in, int colTcSb, int rowTcSb, int Nd) { if (colTcSb*rowTcSb != in.length) throw new IllegalArgumentException("Check Input Array Lengths"); int[] colPermPat = new int[] {0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31}; double[][] y = new double[rowTcSb][colTcSb]; double[] out = new double[rowTcSb*colTcSb - Nd]; //read back into matrix int k=0; for (int j = 0; j < colTcSb; j++){ for (int i = 0; i < rowTcSb; i++){ y[i][colPermPat[j]] = in[k]; k++; } } //unfill matrix k = 0; for (int i = 0; i < rowTcSb; i++){ for (int j = 0; j < colTcSb; j++){ if (Nd > 0) Nd--; else { out[k] = y[i][j]; k++; } } } return out; } private static double[] subBlockDeInterleaver2(double[] in, int colTcSb, int rowTcSb, int Nd) { if (colTcSb*rowTcSb != in.length) throw new IllegalArgumentException("Check Input Array Lengths"); int[] colPermPat = new int[] {0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31}; int[] pi = new int[colTcSb*rowTcSb]; double[] out = new double[rowTcSb*colTcSb - Nd]; for (int i = 0; i < colTcSb*rowTcSb; i++) pi[i] = (colPermPat[(int)Math.floor((double)i/rowTcSb)] + colTcSb*(i%rowTcSb)+1) % (colTcSb*rowTcSb); for (int i = 0; i < colTcSb*rowTcSb; i++) { if (pi[i] < Nd) ; //null else out[pi[i]-Nd] = in[i]; } return out; } static int crc_xmodem_update (int crc, byte data) { int i; crc = crc ^ ((int)data << 8); for (i=0; i<8; i++) { if ((crc & 0x8000) != 0) crc = (crc << 1) ^ 0x1021; else crc <<= 1; } return crc & 0xFFFF; } public static byte hamming_decode84(byte input) { byte s = 0; byte o = input; if ( (input&(1<<7))>0 ^ (input&(1<<2))>0 ^ (input&(1<<1))>0 ^ (input&(1<<0))>0 ) s |= (1<<3); if ( (input&(1<<6))>0 ^ (input&(1<<3))>0 ^ (input&(1<<1))>0 ^ (input&(1<<0))>0 ) s |= (1<<2); if ( (input&(1<<5))>0 ^ (input&(1<<3))>0 ^ (input&(1<<2))>0 ^ (input&(1<<0))>0 ) s |= (1<<1); if ( (input&(1<<4))>0 ^ (input&(1<<3))>0 ^ (input&(1<<2))>0 ^ (input&(1<<1))>0 ) s |= (1<<0); return o; } public static byte hamming_encode84(byte input) { byte o = (byte) (input & 0xF); if ( (input&(1<<3))>0 ^ (input&(1<<2))>0 ^ (input&(1<<1))>0 ) o |= (1<<4); if ( (input&(1<<3))>0 ^ (input&(1<<2))>0 ^ (input&(1<<0))>0 ) o |= (1<<5); if ( (input&(1<<3))>0 ^ (input&(1<<1))>0 ^ (input&(1<<0))>0 ) o |= (1<<6); if ( (input&(1<<2))>0 ^ (input&(1<<1))>0 ^ (input&(1<<0))>0 ) o |= (1<<7); return o; } }