Files
sdrtrunk/src/module/decode/p25/message/hdu/HDUMessage.java
Denny e4323df347 Resolves issue #244 - updates the P25 IMBE audio module to correctly
detect encrypted audio state so that encrypted audio is not (incorrectly)
decoded and played back to the user.

Processes HDU and LDU2 messages to identify encrypted call state.  Once
established, all subsequent LDU1/2 messages are decoded only if the call
is unencrypted.  If an LDU1 message is received out-of-sequence, meaning
that the initial HDU message was missed, then the LDU1 is cached until
we receive an LDU2 message and correctly determine the call encryption
state.
2017-05-28 07:17:09 -04:00

274 lines
10 KiB
Java

package module.decode.p25.message.hdu;
import module.decode.p25.message.P25Message;
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.Golay18;
import edac.ReedSolomon_63_47_17;
public class HDUMessage extends P25Message
{
private final static Logger mLog = LoggerFactory.getLogger( HDUMessage.class );
public static final int[] MESSAGE_INDICATOR_A = { 64,65,66,67,68,69,82,83,
84,85,86,87,100,101,102,103,104,105,118,119,120,121,122,123,136,137,138,
139,140,141,154,155,156,157,158,159 };
public static final int[] MESSAGE_INDICATOR_B = { 172,173,174,175,176,177,
190,191,192,193,194,195,208,209,210,211,212,213,226,227,228,229,230,231,
244,245,246,247,248,249,262,263,264,265,266,267 };
public static final int[] VENDOR_ID = { 280,281,282,283,284,285,298,299 };
public static final int[] ALGORITHM_ID = { 300,301,302,303,316,317,318,319 };
public static final int[] KEY_ID = { 320,321,334,335,336,337,338,339,352,
353,354,355,356,357,370,371 };
public static final int[] TALKGROUP_ID = { 372,373,374,375,388,389,390,391,
392,393,406,407,408,409,410,411 };
public static final int[] GOLAY_WORD_STARTS = { 64,82,100,118,136,154,172,
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[] 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 };
public static final int[] RS_HEX_3 = { 478,479,480,481,482,483 };
public static final int[] RS_HEX_4 = { 496,497,498,499,500,501 };
public static final int[] RS_HEX_5 = { 514,515,516,517,518,519 };
public static final int[] RS_HEX_6 = { 532,533,534,535,536,537 };
public static final int[] RS_HEX_7 = { 550,551,552,553,554,555 };
public static final int[] RS_HEX_8 = { 568,569,570,571,572,573 };
public static final int[] RS_HEX_9 = { 586,587,588,589,590,591 };
public static final int[] RS_HEX_10 = { 604,605,606,607,608,609 };
public static final int[] RS_HEX_11 = { 622,623,624,625,626,627 };
public static final int[] RS_HEX_12 = { 640,641,642,643,644,645 };
public static final int[] RS_HEX_13 = { 658,659,660,661,662,663 };
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( 8 );
public HDUMessage( 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();
}
private void checkCRC()
{
/* Golay( 18,6,18 ) error detection and correction */
for( int index: GOLAY_WORD_STARTS )
{
Golay18.checkAndCorrect( mMessage, index );
}
mCRC[ 1 ] = CRC.PASSED;
/* Reed-Solomon( 36,20,17 ) error detection and correction
* Check the Reed-Solomon parity bits. The RS decoder expects the link
* control data and reed solomon parity hex codewords in reverse order.
* The RS(24,12,13) code used by P25 removes the left-hand 47 data hex
* words, so we replace them with zeros. */
int[] input = new int [63];
int[] output = new int [63];
input[ 0 ] = mMessage.getInt( RS_HEX_15 );
input[ 1 ] = mMessage.getInt( RS_HEX_14 );
input[ 2 ] = mMessage.getInt( RS_HEX_13 );
input[ 3 ] = mMessage.getInt( RS_HEX_12 );
input[ 4 ] = mMessage.getInt( RS_HEX_11 );
input[ 5 ] = mMessage.getInt( RS_HEX_10 );
input[ 6 ] = mMessage.getInt( RS_HEX_9 );
input[ 7 ] = mMessage.getInt( RS_HEX_8 );
input[ 8 ] = mMessage.getInt( RS_HEX_7 );
input[ 9 ] = mMessage.getInt( RS_HEX_6 );
input[ 10 ] = mMessage.getInt( RS_HEX_5 );
input[ 11 ] = mMessage.getInt( RS_HEX_4 );
input[ 12 ] = mMessage.getInt( RS_HEX_3 );
input[ 13 ] = mMessage.getInt( RS_HEX_2 );
input[ 14 ] = mMessage.getInt( RS_HEX_1 );
input[ 15 ] = mMessage.getInt( RS_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 );
if( irrecoverableErrors )
{
mCRC[ 2 ] = CRC.FAILED_CRC;
}
else
{
mCRC[ 2 ] = CRC.PASSED;
/* Only fix the codeword data hex codewords */
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 );
}
}
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();
sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " TALKGROUP:" + getTalkgroupID() );
Encryption encryption = getEncryption();
if( encryption != Encryption.UNENCRYPTED )
{
sb.append( " ENCRYPTION:" );
if( encryption == Encryption.UNKNOWN )
{
sb.append( "UNK ALGO ID:" );
sb.append( mMessage.getInt( ALGORITHM_ID ) );
}
else
{
sb.append( encryption.name() );
}
sb.append( " KEY ID:" + getKeyID() );
sb.append( " MSG INDICATOR:" + getMessageIndicator() );
}
else
{
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 boolean isEncryptedAudio()
{
return getEncryption() != Encryption.UNENCRYPTED;
}
public int getKeyID()
{
return mMessage.getInt( KEY_ID );
}
public String getTalkgroupID()
{
return mMessage.getHex( TALKGROUP_ID, 4 );
}
@Override
public String getToID()
{
return getTalkgroupID();
}
public boolean isValid()
{
return mCRC[ 2 ] != null && mCRC[ 2 ] != CRC.FAILED_CRC;
}
}