package module.decode.p25.message.ldu; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; import edac.CRC; import edac.Hamming10; import edac.ReedSolomon_63_47_17; public class LDU2Message extends LDUMessage { private final static Logger mLog = LoggerFactory.getLogger( LDU2Message.class ); public static final int[] MESSAGE_INDICATOR_A = { 352,353,354,355,356,357, 362,363,364,365,366,367,372,373,374,375,376,377,382,383,384,385,386,387, 536,537,538,539,540,541,546,547,548,549,550,551 }; public static final int[] MESSAGE_INDICATOR_B = { 556,557,558,559,560,561, 566,567,568,569,570,571,720,721,722,723,724,725,730,731,732,733,734,735, 740,741,742,743,744,745,750,751,752,753,754,755 }; public static final int[] ALGORITHM_ID = { 904,905,906,907,908,909,914,915 }; public static final int[] KEY_ID = { 916,917,918,919,924,925,926,927,928, 929,934,935,936,937,938,939 }; public static final int[] GOLAY_WORD_STARTS = { 352,362,372,382,536,546,556,566,720,730,740,750,904,914,924,934,1088, 1098,1108,1118,1272,1282,1292,1302 }; public static final int[] CW_HEX_0 = { 352,353,354,355,356,357 }; public static final int[] CW_HEX_1 = { 362,363,364,365,366,367 }; public static final int[] CW_HEX_2 = { 372,373,374,375,376,377 }; public static final int[] CW_HEX_3 = { 382,383,384,385,386,387 }; public static final int[] CW_HEX_4 = { 536,537,538,539,540,541 }; public static final int[] CW_HEX_5 = { 546,547,548,549,550,551 }; public static final int[] CW_HEX_6 = { 556,557,558,559,560,561 }; public static final int[] CW_HEX_7 = { 566,567,568,569,570,571 }; public static final int[] CW_HEX_8 = { 720,721,722,723,724,725 }; public static final int[] CW_HEX_9 = { 730,731,732,733,734,735 }; public static final int[] CW_HEX_10 = { 740,741,742,743,744,745 }; public static final int[] CW_HEX_11 = { 750,751,752,753,754,755 }; public static final int[] CW_HEX_12 = { 904,905,906,907,908,909 }; public static final int[] CW_HEX_13 = { 914,915,916,917,918,919 }; public static final int[] CW_HEX_14 = { 924,925,926,927,928,929 }; public static final int[] CW_HEX_15 = { 934,935,936,937,938,939 }; public static final int[] RS_HEX_0 = { 1088,1089,1090,1091,1092,1093 }; public static final int[] RS_HEX_1 = { 1098,1099,1100,1101,1102,1103 }; public static final int[] RS_HEX_2 = { 1108,1109,1110,1111,1112,1113 }; public static final int[] RS_HEX_3 = { 1118,1119,1120,1121,1122,1123 }; public static final int[] RS_HEX_4 = { 1272,1273,1274,1275,1276,1277 }; public static final int[] RS_HEX_5 = { 1282,1283,1284,1285,1286,1287 }; public static final int[] RS_HEX_6 = { 1292,1293,1294,1295,1296,1297 }; public static final int[] RS_HEX_7 = { 1302,1303,1304,1305,1306,1307 }; /* Reed-Solomon(24,16,9) code protects the encryption sync word. Maximum * correctable errors are: Hamming Distance(9) / 2 = 4 */ public static final ReedSolomon_63_47_17 mReedSolomonDecoder = new ReedSolomon_63_47_17( 4 ); public LDU2Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) { super( message, duid, aliasList ); /* NID CRC is checked in the message framer, thus a constructed message * means it passed the CRC */ mCRC = new CRC[ 3 ]; mCRC[ 0 ] = CRC.PASSED; checkCRC(); } @Override public boolean isEncrypted() { Encryption encryption = getEncryption(); return encryption != Encryption.UNENCRYPTED && encryption != Encryption.UNKNOWN; } private void checkCRC() { mCRC[ 1 ] = CRC.PASSED; /* Hamming( 10,6,3 ) error detection and correction */ for( int index: GOLAY_WORD_STARTS ) { int errors = Hamming10.checkAndCorrect( mMessage, index ); if( errors > 1 ) { mCRC[ 1 ] = CRC.FAILED_CRC; } } /* Reed-Solomon( 24,16,9 ) error detection and correction * Check the Reed-Solomon parity bits. The RS decoder expects the code * words and reed solomon parity hex codewords in reverse order. * * Since this is a truncated RS(63) codes, we pad the code with zeros */ int[] input = new int [63]; int[] output = new int [63]; input[ 0 ] = mMessage.getInt( RS_HEX_7 ); input[ 1 ] = mMessage.getInt( RS_HEX_6 ); input[ 2 ] = mMessage.getInt( RS_HEX_5 ); input[ 3 ] = mMessage.getInt( RS_HEX_4 ); input[ 4 ] = mMessage.getInt( RS_HEX_3 ); input[ 5 ] = mMessage.getInt( RS_HEX_2 ); input[ 6 ] = mMessage.getInt( RS_HEX_1 ); input[ 7 ] = mMessage.getInt( RS_HEX_0 ); input[ 8 ] = mMessage.getInt( CW_HEX_15 ); input[ 9 ] = mMessage.getInt( CW_HEX_14 ); input[ 10 ] = mMessage.getInt( CW_HEX_13 ); input[ 11 ] = mMessage.getInt( CW_HEX_12 ); input[ 12 ] = mMessage.getInt( CW_HEX_11 ); input[ 13 ] = mMessage.getInt( CW_HEX_10 ); input[ 14 ] = mMessage.getInt( CW_HEX_9 ); input[ 15 ] = mMessage.getInt( CW_HEX_8 ); input[ 16 ] = mMessage.getInt( CW_HEX_7 ); input[ 17 ] = mMessage.getInt( CW_HEX_6 ); input[ 18 ] = mMessage.getInt( CW_HEX_5 ); input[ 19 ] = mMessage.getInt( CW_HEX_4 ); input[ 20 ] = mMessage.getInt( CW_HEX_3 ); input[ 21 ] = mMessage.getInt( CW_HEX_2 ); input[ 22 ] = mMessage.getInt( CW_HEX_1 ); input[ 23 ] = mMessage.getInt( CW_HEX_0 ); /* indexes 24 - 62 are defaulted to zero */ boolean irrecoverableErrors = mReedSolomonDecoder.decode( input, output ); if( irrecoverableErrors ) { mCRC[ 2 ] = CRC.FAILED_CRC; } else { /* Since we've detected that we can correct the RS words, mark the * hamming crc as corrected as well, if it is currently failed */ if( mCRC[ 1 ] == CRC.FAILED_CRC ) { mCRC[ 1 ] = CRC.CORRECTED; } mCRC[ 2 ] = CRC.PASSED; /* Only fix the codeword data hex codewords */ repairHexCodeword( input, output, 8, CW_HEX_15 ); repairHexCodeword( input, output, 9, CW_HEX_14 ); repairHexCodeword( input, output, 10, CW_HEX_13 ); repairHexCodeword( input, output, 11, CW_HEX_12 ); repairHexCodeword( input, output, 12, CW_HEX_11 ); repairHexCodeword( input, output, 13, CW_HEX_10 ); repairHexCodeword( input, output, 14, CW_HEX_9 ); repairHexCodeword( input, output, 15, CW_HEX_8 ); repairHexCodeword( input, output, 16, CW_HEX_7 ); repairHexCodeword( input, output, 17, CW_HEX_6 ); repairHexCodeword( input, output, 18, CW_HEX_5 ); repairHexCodeword( input, output, 19, CW_HEX_4 ); repairHexCodeword( input, output, 20, CW_HEX_3 ); repairHexCodeword( input, output, 21, CW_HEX_2 ); repairHexCodeword( input, output, 22, CW_HEX_1 ); repairHexCodeword( input, output, 23, CW_HEX_0 ); } } private void repairHexCodeword( int[] input, int[] output, int index, int[] indexSet ) { if( input[ index ] != output[ index ] ) { mMessage.load( indexSet[ 0 ], 6, output[ index ] ); mCRC[ 2 ] = CRC.CORRECTED; } } @Override public String getMessage() { StringBuilder sb = new StringBuilder(); Encryption encryption = getEncryption(); if( encryption != Encryption.UNENCRYPTED ) { sb.append( "NAC:" ); sb.append( getNAC() ); sb.append( " " ); sb.append( getDUID().getLabel() ); sb.append( " ENCRYPTED VOICE:" ); sb.append( encryption.name() ); sb.append( " KEY ID:" + getKeyID() ); sb.append( " MSG INDICATOR:" + getMessageIndicator() ); } else { sb.append( super.getMessageStub() ); sb.append( "UNENCRYPTED" ); } sb.append( " " ); sb.append( mMessage.toString() ); return sb.toString(); } public String getMessageIndicator() { StringBuilder sb = new StringBuilder(); sb.append( mMessage.getHex( MESSAGE_INDICATOR_A, 9 ) ); sb.append( mMessage.getHex( MESSAGE_INDICATOR_B, 9 ) ); return sb.toString(); } public Encryption getEncryption() { return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); } public int getKeyID() { return mMessage.getInt( KEY_ID ); } }