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 );
}
}