diff --git a/.classpath b/.classpath index 050a1c6c..5ac3191b 100644 --- a/.classpath +++ b/.classpath @@ -24,6 +24,6 @@ - + diff --git a/src/decode/p25/P25MessageProcessor.java b/src/decode/p25/P25MessageProcessor.java index 9128a747..f23c064b 100644 --- a/src/decode/p25/P25MessageProcessor.java +++ b/src/decode/p25/P25MessageProcessor.java @@ -37,14 +37,12 @@ public class P25MessageProcessor implements Listener @Override public void receive( Message message ) { - /* Debug - prints out the imbe message frames */ - if( message instanceof LDUMessage ) - { - ((LDUMessage)message).getIMBEFrames(); - } - - - + /** + * Capture frequency band identifier messages and inject them into any + * messages that require band information in order to calculate the + * up-link and down-link frequencies for any numeric channel references + * contained within the message. + */ if( message.isValid() ) { /* Insert band identifier update messages into channel-type messages */ @@ -70,7 +68,14 @@ public class P25MessageProcessor implements Listener mBandIdentifierMap.put( bandIdentifier.getIdentifier(), bandIdentifier ); } - + } + + /** + * Broadcast all valid messages and any LDU voice messages regardless if + * they are valid or not, so that we don't miss any voice frames + */ + if( message.isValid() || message instanceof LDUMessage ) + { mBroadcaster.broadcast( message ); } } diff --git a/src/decode/p25/message/hdu/HDUMessage.java b/src/decode/p25/message/hdu/HDUMessage.java index 9d3c63ae..5094e2d4 100644 --- a/src/decode/p25/message/hdu/HDUMessage.java +++ b/src/decode/p25/message/hdu/HDUMessage.java @@ -33,26 +33,26 @@ public class HDUMessage extends P25Message 190,208,226,244,262,280,298,316,334,352,370,388,406,424,442,460,478, 496,514,532,550,568,586,604,622,640,658,676,694 }; - public static final int[] LC_HEX_0 = { 64,65,66,67,68,69 }; - public static final int[] LC_HEX_1 = { 82,83,84,85,86,87 }; - public static final int[] LC_HEX_2 = { 100,101,102,103,104,105 }; - public static final int[] LC_HEX_3 = { 118,119,120,121,122,123 }; - public static final int[] LC_HEX_4 = { 136,137,138,139,140,141 }; - public static final int[] LC_HEX_5 = { 154,155,156,157,158,159 }; - public static final int[] LC_HEX_6 = { 172,173,174,175,176,177 }; - public static final int[] LC_HEX_7 = { 190,191,192,193,194,195 }; - public static final int[] LC_HEX_8 = { 208,209,210,211,212,213 }; - public static final int[] LC_HEX_9 = { 226,227,228,229,230,231 }; - public static final int[] LC_HEX_10 = { 244,245,246,247,248,249 }; - public static final int[] LC_HEX_11 = { 262,263,264,265,266,267 }; - public static final int[] LC_HEX_12 = { 280,281,282,283,284,285 }; - public static final int[] LC_HEX_13 = { 298,299,300,301,302,303 }; - public static final int[] LC_HEX_14 = { 316,317,318,319,320,321 }; - public static final int[] LC_HEX_15 = { 334,335,336,337,338,339 }; - public static final int[] LC_HEX_16 = { 352,353,354,355,356,357 }; - public static final int[] LC_HEX_17 = { 370,371,372,373,374,375 }; - public static final int[] LC_HEX_18 = { 388,389,390,391,392,393 }; - public static final int[] LC_HEX_19 = { 406,407,408,409,410,411 }; + public static final int[] CW_HEX_0 = { 64,65,66,67,68,69 }; + public static final int[] CW_HEX_1 = { 82,83,84,85,86,87 }; + public static final int[] CW_HEX_2 = { 100,101,102,103,104,105 }; + public static final int[] CW_HEX_3 = { 118,119,120,121,122,123 }; + public static final int[] CW_HEX_4 = { 136,137,138,139,140,141 }; + public static final int[] CW_HEX_5 = { 154,155,156,157,158,159 }; + public static final int[] CW_HEX_6 = { 172,173,174,175,176,177 }; + public static final int[] CW_HEX_7 = { 190,191,192,193,194,195 }; + public static final int[] CW_HEX_8 = { 208,209,210,211,212,213 }; + public static final int[] CW_HEX_9 = { 226,227,228,229,230,231 }; + public static final int[] CW_HEX_10 = { 244,245,246,247,248,249 }; + public static final int[] CW_HEX_11 = { 262,263,264,265,266,267 }; + public static final int[] CW_HEX_12 = { 280,281,282,283,284,285 }; + public static final int[] CW_HEX_13 = { 298,299,300,301,302,303 }; + public static final int[] CW_HEX_14 = { 316,317,318,319,320,321 }; + public static final int[] CW_HEX_15 = { 334,335,336,337,338,339 }; + public static final int[] CW_HEX_16 = { 352,353,354,355,356,357 }; + public static final int[] CW_HEX_17 = { 370,371,372,373,374,375 }; + public static final int[] CW_HEX_18 = { 388,389,390,391,392,393 }; + public static final int[] CW_HEX_19 = { 406,407,408,409,410,411 }; public static final int[] RS_HEX_0 = { 424,425,426,427,428,429 }; public static final int[] RS_HEX_1 = { 442,443,444,445,446,447 }; public static final int[] RS_HEX_2 = { 460,461,462,463,464,465 }; @@ -70,8 +70,10 @@ public class HDUMessage extends P25Message public static final int[] RS_HEX_14 = { 676,677,678,679,680,681 }; public static final int[] RS_HEX_15 = { 694,695,696,697,698,699 }; + /* Reed-Solomon(36,20,17) code protects the header word. Maximum + * correctable errors are: Hamming Distance(17) / 2 = 8 */ public static final ReedSolomon_63_47_17 mReedSolomonDecoder = - new ReedSolomon_63_47_17(); + new ReedSolomon_63_47_17( 8 ); public HDUMessage( BinaryMessage message, DataUnitID duid, AliasList aliasList ) @@ -121,26 +123,26 @@ public class HDUMessage extends P25Message input[ 14 ] = mMessage.getInt( RS_HEX_1 ); input[ 15 ] = mMessage.getInt( RS_HEX_0 ); - input[ 16 ] = mMessage.getInt( LC_HEX_19 ); - input[ 17 ] = mMessage.getInt( LC_HEX_18 ); - input[ 18 ] = mMessage.getInt( LC_HEX_17 ); - input[ 19 ] = mMessage.getInt( LC_HEX_16 ); - input[ 20 ] = mMessage.getInt( LC_HEX_15 ); - input[ 21 ] = mMessage.getInt( LC_HEX_14 ); - input[ 22 ] = mMessage.getInt( LC_HEX_13 ); - input[ 23 ] = mMessage.getInt( LC_HEX_12 ); - input[ 24 ] = mMessage.getInt( LC_HEX_11 ); - input[ 25 ] = mMessage.getInt( LC_HEX_10 ); - input[ 26 ] = mMessage.getInt( LC_HEX_9 ); - input[ 27 ] = mMessage.getInt( LC_HEX_8 ); - input[ 28 ] = mMessage.getInt( LC_HEX_7 ); - input[ 29 ] = mMessage.getInt( LC_HEX_6 ); - input[ 30 ] = mMessage.getInt( LC_HEX_5 ); - input[ 31 ] = mMessage.getInt( LC_HEX_4 ); - input[ 32 ] = mMessage.getInt( LC_HEX_3 ); - input[ 33 ] = mMessage.getInt( LC_HEX_2 ); - input[ 34 ] = mMessage.getInt( LC_HEX_1 ); - input[ 35 ] = mMessage.getInt( LC_HEX_0 ); + input[ 16 ] = mMessage.getInt( CW_HEX_19 ); + input[ 17 ] = mMessage.getInt( CW_HEX_18 ); + input[ 18 ] = mMessage.getInt( CW_HEX_17 ); + input[ 19 ] = mMessage.getInt( CW_HEX_16 ); + input[ 20 ] = mMessage.getInt( CW_HEX_15 ); + input[ 21 ] = mMessage.getInt( CW_HEX_14 ); + input[ 22 ] = mMessage.getInt( CW_HEX_13 ); + input[ 23 ] = mMessage.getInt( CW_HEX_12 ); + input[ 24 ] = mMessage.getInt( CW_HEX_11 ); + input[ 25 ] = mMessage.getInt( CW_HEX_10 ); + input[ 26 ] = mMessage.getInt( CW_HEX_9 ); + input[ 27 ] = mMessage.getInt( CW_HEX_8 ); + input[ 28 ] = mMessage.getInt( CW_HEX_7 ); + input[ 29 ] = mMessage.getInt( CW_HEX_6 ); + input[ 30 ] = mMessage.getInt( CW_HEX_5 ); + input[ 31 ] = mMessage.getInt( CW_HEX_4 ); + input[ 32 ] = mMessage.getInt( CW_HEX_3 ); + input[ 33 ] = mMessage.getInt( CW_HEX_2 ); + input[ 34 ] = mMessage.getInt( CW_HEX_1 ); + input[ 35 ] = mMessage.getInt( CW_HEX_0 ); /* indexes 36 - 62 are defaulted to zero */ boolean irrecoverableErrors = mReedSolomonDecoder.decode( input, output ); @@ -154,26 +156,26 @@ public class HDUMessage extends P25Message mCRC[ 2 ] = CRC.PASSED; /* Only fix the codeword data hex codewords */ - repairHexCodeword( input, output, 16, LC_HEX_19 ); - repairHexCodeword( input, output, 17, LC_HEX_18 ); - repairHexCodeword( input, output, 18, LC_HEX_17 ); - repairHexCodeword( input, output, 19, LC_HEX_16 ); - repairHexCodeword( input, output, 20, LC_HEX_15 ); - repairHexCodeword( input, output, 21, LC_HEX_14 ); - repairHexCodeword( input, output, 22, LC_HEX_13 ); - repairHexCodeword( input, output, 23, LC_HEX_12 ); - repairHexCodeword( input, output, 24, LC_HEX_11 ); - repairHexCodeword( input, output, 25, LC_HEX_10 ); - repairHexCodeword( input, output, 26, LC_HEX_9 ); - repairHexCodeword( input, output, 27, LC_HEX_8 ); - repairHexCodeword( input, output, 28, LC_HEX_7 ); - repairHexCodeword( input, output, 29, LC_HEX_6 ); - repairHexCodeword( input, output, 30, LC_HEX_5 ); - repairHexCodeword( input, output, 31, LC_HEX_4 ); - repairHexCodeword( input, output, 32, LC_HEX_3 ); - repairHexCodeword( input, output, 33, LC_HEX_2 ); - repairHexCodeword( input, output, 34, LC_HEX_1 ); - repairHexCodeword( input, output, 35, LC_HEX_0 ); + repairHexCodeword( input, output, 16, CW_HEX_19 ); + repairHexCodeword( input, output, 17, CW_HEX_18 ); + repairHexCodeword( input, output, 18, CW_HEX_17 ); + repairHexCodeword( input, output, 19, CW_HEX_16 ); + repairHexCodeword( input, output, 20, CW_HEX_15 ); + repairHexCodeword( input, output, 21, CW_HEX_14 ); + repairHexCodeword( input, output, 22, CW_HEX_13 ); + repairHexCodeword( input, output, 23, CW_HEX_12 ); + repairHexCodeword( input, output, 24, CW_HEX_11 ); + repairHexCodeword( input, output, 25, CW_HEX_10 ); + repairHexCodeword( input, output, 26, CW_HEX_9 ); + repairHexCodeword( input, output, 27, CW_HEX_8 ); + repairHexCodeword( input, output, 28, CW_HEX_7 ); + repairHexCodeword( input, output, 29, CW_HEX_6 ); + repairHexCodeword( input, output, 30, CW_HEX_5 ); + repairHexCodeword( input, output, 31, CW_HEX_4 ); + repairHexCodeword( input, output, 32, CW_HEX_3 ); + repairHexCodeword( input, output, 33, CW_HEX_2 ); + repairHexCodeword( input, output, 34, CW_HEX_1 ); + repairHexCodeword( input, output, 35, CW_HEX_0 ); } } @@ -181,7 +183,6 @@ public class HDUMessage extends P25Message { if( input[ index ] != output[ index ] ) { - mLog.debug( "Fixing index: " + index ); mMessage.load( indexSet[ 0 ], 6, output[ index ] ); mCRC[ 2 ] = CRC.CORRECTED; } diff --git a/src/decode/p25/message/ldu/LDU1Message.java b/src/decode/p25/message/ldu/LDU1Message.java index ee498a6e..34364b1e 100644 --- a/src/decode/p25/message/ldu/LDU1Message.java +++ b/src/decode/p25/message/ldu/LDU1Message.java @@ -2,12 +2,13 @@ package decode.p25.message.ldu; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.P25Interleave; import decode.p25.reference.DataUnitID; import decode.p25.reference.LinkControlOpcode; import decode.p25.reference.Vendor; import decode.p25.reference.VendorLinkControlOpcode; import edac.CRC; +import edac.Hamming10; +import edac.ReedSolomon_63_47_17; public class LDU1Message extends LDUMessage { @@ -25,6 +26,40 @@ public class LDU1Message extends LDUMessage // public static final int[] OCTET_7 = { 732,733,734,735,740,741,742,743 }; // public static final int[] OCTET_8 = { 744,745,750,751,752,753,754,755 }; + 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[] RS_HEX_0 = { 904,905,906,907,908,909 }; + public static final int[] RS_HEX_1 = { 914,915,916,917,918,919 }; + public static final int[] RS_HEX_2 = { 924,925,926,927,928,929 }; + public static final int[] RS_HEX_3 = { 934,935,936,937,938,939 }; + public static final int[] RS_HEX_4 = { 1088,1089,1090,1091,1092,1093 }; + public static final int[] RS_HEX_5 = { 1098,1099,1100,1101,1102,1103 }; + public static final int[] RS_HEX_6 = { 1108,1109,1110,1111,1112,1113 }; + public static final int[] RS_HEX_7 = { 1118,1119,1120,1121,1122,1123 }; + public static final int[] RS_HEX_8 = { 1272,1273,1274,1275,1276,1277 }; + public static final int[] RS_HEX_9 = { 1282,1283,1284,1285,1286,1287 }; + public static final int[] RS_HEX_10 = { 1292,1293,1294,1295,1296,1297 }; + public static final int[] RS_HEX_11 = { 1302,1303,1304,1305,1306,1307 }; + + /* Reed-Solomon(24,12,13) code protects the link control word. Maximum + * correctable errors are: Hamming Distance(13) / 2 = 6 */ + public static final ReedSolomon_63_47_17 mReedSolomonDecoder = + new ReedSolomon_63_47_17( 6 ); + public LDU1Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) { @@ -33,14 +68,12 @@ public class LDU1Message extends LDUMessage /* NID CRC is checked in the message framer, thus a constructed message * means it passed the CRC */ - mCRC = new CRC[ 2 ]; + mCRC = new CRC[ 3 ]; mCRC[ 0 ] = CRC.PASSED; - //TODO: edac on link control octets - checkCRC(); } - + /** * Subclass constructor from an existing LDU1Message */ @@ -51,9 +84,97 @@ public class LDU1Message extends LDUMessage mCRC = message.mCRC; } + 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_11 ); + input[ 1 ] = mMessage.getInt( RS_HEX_10 ); + input[ 2 ] = mMessage.getInt( RS_HEX_9 ); + input[ 3 ] = mMessage.getInt( RS_HEX_8 ); + input[ 4 ] = mMessage.getInt( RS_HEX_7 ); + input[ 5 ] = mMessage.getInt( RS_HEX_6 ); + input[ 6 ] = mMessage.getInt( RS_HEX_5 ); + input[ 7 ] = mMessage.getInt( RS_HEX_4 ); + input[ 8 ] = mMessage.getInt( RS_HEX_3 ); + input[ 9 ] = mMessage.getInt( RS_HEX_2 ); + input[ 10 ] = mMessage.getInt( RS_HEX_1 ); + input[ 11 ] = mMessage.getInt( RS_HEX_0 ); + + 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, 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 diff --git a/src/decode/p25/message/ldu/LDU2Message.java b/src/decode/p25/message/ldu/LDU2Message.java index 90842044..64d37c31 100644 --- a/src/decode/p25/message/ldu/LDU2Message.java +++ b/src/decode/p25/message/ldu/LDU2Message.java @@ -1,22 +1,19 @@ package decode.p25.message.ldu; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import alias.AliasList; import bits.BinaryMessage; import decode.p25.reference.DataUnitID; import decode.p25.reference.Encryption; import edac.CRC; +import edac.Hamming10; +import edac.ReedSolomon_63_47_17; public class LDU2Message extends LDUMessage { -// public static final int[] OCTET_0 = { 352,353,354,355,356,357,362,363 }; -// public static final int[] OCTET_1 = { 364,365,366,367,372,373,374,375 }; -// public static final int[] OCTET_2 = { 376,377,382,383,384,385,386,387 }; -// public static final int[] OCTET_3 = { 536,537,538,539,540,541,546,547 }; -// public static final int[] OCTET_4 = { 548,549,550,551,556,557,558,559 }; -// public static final int[] OCTET_5 = { 560,561,566,567,568,569,570,571 }; -// public static final int[] OCTET_6 = { 720,721,722,723,724,725,730,731 }; -// public static final int[] OCTET_7 = { 732,733,734,735,740,741,742,743 }; -// public static final int[] OCTET_8 = { 744,745,750,751,752,753,754,755 }; + 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, @@ -28,6 +25,40 @@ public class LDU2Message extends LDUMessage 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 ) { @@ -35,10 +66,108 @@ public class LDU2Message extends LDUMessage /* NID CRC is checked in the message framer, thus a constructed message * means it passed the CRC */ - mCRC = new CRC[ 2 ]; + mCRC = new CRC[ 3 ]; mCRC[ 0 ] = CRC.PASSED; + + checkCRC(); } + 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() { @@ -71,12 +200,6 @@ public class LDU2Message extends LDUMessage return sb.toString(); } - /* Temporary override */ - public boolean isValid() - { - return true; - } - public String getMessageIndicator() { StringBuilder sb = new StringBuilder(); @@ -97,6 +220,15 @@ public class LDU2Message extends LDUMessage return mMessage.getInt( KEY_ID ); } - + public static void main( String[] args ) + { + String msg = "001010100000101010011010001001001001010011000111000111110111001100000010000101001011110111111000001100111100110000000010001110011011001101101101101101110011110011010000101111001111000011000010110010011110110010000100101101101001100000100000100001011100000000001110011111011000100100000000011011100000111111111101111010110111000111100011010111111110111000000000000000000000000000000000000000000100100001110110101111011000011010010100011001000101001011100101111011000001101100010101111001000100001111011111011001111110100111001100110110000000000000000000000000000000000000000000000101000001011110111010110010000100010110001100001101001110111001000111100100111011011011110101011111011000010100110111001000101000010011101010000000000000000000000000000000000000000001001000001000011011110110011110011001111100100000111000101001110111011110101001000101001100001110001100000000110000110000001011110000000101100110000011100000000000000000000000000000000111011001010000111111111111110000110110011000100101011000001111110000101000101111010101010101000100110111000100100111110010101010000100111010111010111010001011010010001011011001001001011100000101000010010010111100000110010000101010000110101100101111100110111100010001011011110101001011010100010000111000010000001001001001101000100110101011110100111100011111110101100000011011011001000001010100011001010100110000111100101011100110110011111100011011111110100111011101011111100001111011000101100011101000110001101000000000000000000000000000000000010001101101011101111010101010000111011011101001011111001110111001000010101011010000001011010111010000011011001100010110001010001000010001101101"; + + BinaryMessage message = BinaryMessage.load( msg ); + LDU2Message ldu2 = new LDU2Message( message, DataUnitID.LDU2, null ); + + mLog.debug( ldu2.getMessage() ); + mLog.debug( "CRC: " + CRC.format( ldu2.getCRCResults() )); + } } diff --git a/src/decode/p25/message/ldu/LDUMessage.java b/src/decode/p25/message/ldu/LDUMessage.java index 883731ea..66a0df80 100644 --- a/src/decode/p25/message/ldu/LDUMessage.java +++ b/src/decode/p25/message/ldu/LDUMessage.java @@ -68,9 +68,9 @@ public class LDUMessage extends P25Message return mMessage.getHex( LOW_SPEED_DATA, 4 ); } - /* Temporary override */ public boolean isValid() { +// return mCRC[ 2 ] != null && mCRC[ 2 ] != CRC.FAILED_CRC; return true; } diff --git a/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java b/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java index 08042085..a33424f6 100644 --- a/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java +++ b/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java @@ -49,8 +49,10 @@ public class TDULinkControlMessage extends P25Message public static final int[] OPCODE = { 66,67,68,69,70,71 }; public static final int[] VENDOR = { 72,73,74,75,88,89,90,91 }; + /* Reed-Solomon(24,12,13) code protects the link control word. Maximum + * correctable errors are: Hamming Distance(13) / 2 = 6 */ public static final ReedSolomon_63_47_17 mReedSolomonDecoder = - new ReedSolomon_63_47_17(); + new ReedSolomon_63_47_17( 6 ); public TDULinkControlMessage( BinaryMessage message, DataUnitID duid, AliasList aliasList ) diff --git a/src/edac/BerlekempMassey_63.java b/src/edac/BerlekempMassey_63.java index f3875a9a..5fa014a9 100644 --- a/src/edac/BerlekempMassey_63.java +++ b/src/edac/BerlekempMassey_63.java @@ -95,9 +95,9 @@ public class BerlekempMassey_63 * * alpha_to = 2 is the primitive element of GF( 2**mm ) * - * @param generator_polinomial + * @param generator_polynomial */ - private void generate_gf( int[] generator_polinomial ) + private void generate_gf( int[] generator_polynomial ) { int i; int mask = 1; @@ -109,7 +109,7 @@ public class BerlekempMassey_63 alpha_to[ i ] = mask; index_of[ alpha_to[ i ] ] = i; - if( generator_polinomial[ i ] != 0 ) + if( generator_polynomial[ i ] != 0 ) { alpha_to[ MM ] ^= mask; } diff --git a/src/edac/Hamming10.java b/src/edac/Hamming10.java new file mode 100644 index 00000000..b1b2b17e --- /dev/null +++ b/src/edac/Hamming10.java @@ -0,0 +1,116 @@ +package edac; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import bits.BinaryMessage; + +public class Hamming10 +{ + private final static Logger mLog = LoggerFactory.getLogger( Hamming10.class ); + + private static int[] CHECKSUMS = new int[] { 0xE,0xD,0xB,0X7,0x3,0xC }; + + /** + * Performs error detection and correction of any single-bit errors. This + * is a truncated version of the Hamming15 class. + * + * @param frame - binary frame containing a Hamming(10,6,3) protected field + * + * @param startIndex - offset to the first bit of the field + * + * @return - 0 = no errors + * 1 = a single-bit error was corrected + */ + public static int checkAndCorrect( BinaryMessage frame, int startIndex ) + { + int syndrome = getSyndrome( frame, startIndex ); + + switch( syndrome ) + { + case 0: + return 0; + case 1: + frame.flip( startIndex + 9 ); //Parity 1 + return 1; + case 2: + frame.flip( startIndex + 8 ); //Parity 2 + return 1; + case 3: + frame.flip( startIndex + 4 ); //Data 2 + return 1; + case 4: + frame.flip( startIndex + 7 ); //Parity 4 + return 1; + case 5: + return 2; + case 6: + return 2; + case 7: + frame.flip( startIndex + 3 ); //Data 3 + return 1; + case 8: + frame.flip( startIndex + 6 ); //Parity 8 + return 1; + case 9: + return 2; + case 10: + return 2; + case 11: + frame.flip( startIndex + 2 ); //Data 4 + return 1; + case 12: + frame.flip( startIndex + 5 ); //Data 1 + return 1; + case 13: + frame.flip( startIndex + 1 ); //Data 5 + return 1; + case 14: + frame.flip( startIndex + 0 ); //Data 6 + return 1; + case 15: + return 2; + } + + /* We'll never get to here */ + return 2; + } + + /** + * Calculates the checksum (Parity 8,4,2,1) for data (6 <> 1 ) bits. + * @param frame - frame containing hamming(10) protected word + * @param startIndex - start bit index of the hamming protected word + * @return parity value, 0 - 15 + */ + private static int calculateChecksum( BinaryMessage frame, int startIndex ) + { + int calculated = 0; //Starting value + + /* Iterate the set bits and XOR running checksum with lookup value */ + for (int i = frame.nextSetBit( startIndex ); + i >= startIndex && i < startIndex + 6; + i = frame.nextSetBit( i+1 ) ) + { + calculated ^= CHECKSUMS[ i - startIndex ]; + } + + return calculated; + } + + /** + * Calculates the syndrome - xor of the calculated checksum and the actual + * checksum. + * + * @param frame - binary frame containing a hamming(15,11,4) protected word + * @param startIndex - of bit 0 of the hamming protected word + * @return - 0 (no errors) or 1 (single bit error corrected) + */ + private static int getSyndrome( BinaryMessage frame, int startIndex ) + { + int calculated = calculateChecksum( frame, startIndex ); + + int checksum = frame.getInt( startIndex + 6, startIndex + 9 ); + + return ( checksum ^ calculated ); + } +} diff --git a/src/edac/ReedSolomon_63_47_17.java b/src/edac/ReedSolomon_63_47_17.java index 590686ac..55d62211 100644 --- a/src/edac/ReedSolomon_63_47_17.java +++ b/src/edac/ReedSolomon_63_47_17.java @@ -21,11 +21,18 @@ package edac; public class ReedSolomon_63_47_17 extends BerlekempMassey_63 { /** - * Reed-Solomon RS(24,12,13) decoder + * Reed-Solomon RS(63,47,17) decoder. This can also be used for error detection + * and correction of the following RS codes: + * + * RS(36,20,17) - max 8 errors + * RS(24,16,9) - max 4 errors + * RS(24,12,13) - max 6 errors + * + * The maximum correctable errors is determined by (n-k)/2, or hamming + * distance divided by 2. */ - public ReedSolomon_63_47_17() + public ReedSolomon_63_47_17( int maximumCorrectableErrors ) { - /* TT = 6 = maximum correctable bit errors */ - super( 6 ); + super( maximumCorrectableErrors ); } }