diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 828be84c..8dc31d68 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -421,7 +421,7 @@ public class P25DecoderState extends DecoderState mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL) .aliasList(getAliasList()) .channel(mCurrentChannel) - .details((hdu.isEncrypted() ? "ENCRYPTED " : "")) + .details((hdu.isEncryptedAudio() ? "ENCRYPTED " : "")) .frequency(mCurrentChannelFrequency) .to(to) .build(); @@ -1107,7 +1107,7 @@ public class P25DecoderState extends DecoderState broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) .aliasList(getAliasList()) .channel(gvcu.getChannelA()) - .details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) + .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) .frequency(gvcu.getDownlinkFrequencyA()) .to(userA) .build()); @@ -1122,7 +1122,7 @@ public class P25DecoderState extends DecoderState broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) .aliasList(getAliasList()) .channel(gvcu.getChannelB()) - .details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) + .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) .frequency(gvcu.getDownlinkFrequencyB()) .to(userB) .build()); @@ -1151,7 +1151,7 @@ public class P25DecoderState extends DecoderState broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) .aliasList(getAliasList()) .channel(gvcue.getTransmitChannel()) - .details((gvcue.isEncrypted() ? "ENCRYPTED" : "")) + .details((gvcue.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) .frequency(gvcue.getDownlinkFrequency()) .to(group) .build()); @@ -1187,7 +1187,7 @@ public class P25DecoderState extends DecoderState if(mCurrentCallEvent.getDetails() == null) { mCurrentCallEvent.setDetails( - (gvcuser.isEncrypted() ? "ENCRYPTED " : "") + + (gvcuser.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + (gvcuser.isEmergency() ? "EMERGENCY " : "")); broadcast(mCurrentCallEvent); @@ -1399,7 +1399,7 @@ public class P25DecoderState extends DecoderState if(mCurrentCallEvent.getDetails() == null) { mCurrentCallEvent.setDetails( - (tivcu.isEncrypted() ? "ENCRYPTED " : "") + + (tivcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + (tivcu.isEmergency() ? "EMERGENCY " : "")); broadcast(mCurrentCallEvent); @@ -1482,7 +1482,7 @@ public class P25DecoderState extends DecoderState if(mCurrentCallEvent.getDetails() == null) { mCurrentCallEvent.setDetails( - (uuvcu.isEncrypted() ? "ENCRYPTED " : "") + + (uuvcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + (uuvcu.isEmergency() ? "EMERGENCY " : "")); broadcast(mCurrentCallEvent); diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java index 14279427..a8f7383f 100644 --- a/src/module/decode/p25/audio/P25AudioModule.java +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -12,6 +12,9 @@ import jmbe.iface.AudioConverter; import message.IMessageListener; import message.Message; import module.Module; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.ldu.LDU2Message; import module.decode.p25.message.ldu.LDUMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,14 +28,18 @@ public class P25AudioModule extends Module implements Listener, IAudioP private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class); private static final String IMBE_CODEC = "IMBE"; - private static boolean mLibraryLoadStatusLogged = false; + private boolean mCanConvertAudio = false; + private boolean mEncryptedCall = false; + private boolean mEncryptedCallStateEstablished = false; + private AudioConverter mAudioConverter; private Listener mAudioPacketListener; private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f); private Metadata mMetadata; + private LDU1Message mCachedLDU1Message = null; public P25AudioModule(Metadata metadata) { @@ -76,32 +83,73 @@ public class P25AudioModule extends Module implements Listener, IAudioP } /** - * Primary processing method for p25 imbe audio frame messages. Each LDU - * audio message contains 9 imbe voice frames. Each frame is converted to - * audio when the JMBE library is loaded, processed by auto-gain, and - * broadcast to the audio packet listener. + * Processes call header (HDU) and voice frame (LDU1/LDU2) messages to decode audio and to determine the + * encrypted audio status of a call event. Only the HDU and LDU2 messages convey encrypted call status. If an + * LDU1 message is received without a preceding HDU message, then the LDU1 message is cached until the first + * LDU2 message is received and the encryption state can be determined. Both the LDU1 and the LDU2 message are + * then processed for audio if the call is unencrypted. */ public void receive(Message message) { - if (mCanConvertAudio && mAudioPacketListener != null) + if(mCanConvertAudio && mAudioPacketListener != null) { - if (message instanceof LDUMessage) + if(mEncryptedCallStateEstablished) { - LDUMessage ldu = (LDUMessage) message; - - /* Only process unencrypted audio frames */ - if (!(ldu.isValid() && ldu.isEncrypted())) + if(message instanceof LDUMessage) { - for (byte[] frame : ldu.getIMBEFrames()) - { - float[] audio = mAudioConverter.decode(frame); - - audio = mGain.apply(audio); - - mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf())); - } + processAudio((LDUMessage)message); } } + else + { + if(message instanceof HDUMessage) + { + mEncryptedCallStateEstablished = true; + mEncryptedCall = ((HDUMessage)message).isEncryptedAudio(); + } + else if(message instanceof LDU1Message) + { + //When we receive an LDU1 message with first receiving the HDU message, cache the LDU1 Message + //until we can determine the encrypted call state from the next LDU2 message + mCachedLDU1Message = (LDU1Message)message; + } + else if(message instanceof LDU2Message) + { + mEncryptedCallStateEstablished = true; + LDU2Message ldu2 = (LDU2Message)message; + mEncryptedCall = ldu2.isEncryptedAudio(); + + if(mCachedLDU1Message != null) + { + processAudio(mCachedLDU1Message); + mCachedLDU1Message = null; + } + + processAudio(ldu2); + } + } + } + } + + /** + * Processes an audio packet by decoding the IMBE audio frames and rebroadcasting them as PCM audio packets. + */ + private void processAudio(LDUMessage ldu) + { + if(!mEncryptedCall) + { + for(byte[] frame : ldu.getIMBEFrames()) + { + float[] audio = mAudioConverter.decode(frame); + + audio = mGain.apply(audio); + + mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf())); + } + } + else + { + //Encrypted audio processing not implemented } } @@ -119,18 +167,18 @@ public class P25AudioModule extends Module implements Listener, IAudioP @SuppressWarnings("rawtypes") Class temp = Class.forName("jmbe.JMBEAudioLibrary"); - library = (AudioConversionLibrary) temp.newInstance(); + library = (AudioConversionLibrary)temp.newInstance(); - if ((library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 && - library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1) + if((library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 && + library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1) { mAudioConverter = library.getAudioConverter(IMBE_CODEC, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO); - if (mAudioConverter != null) + if(mAudioConverter != null) { mCanConvertAudio = true; - if (!mLibraryLoadStatusLogged) + if(!mLibraryLoadStatusLogged) { StringBuilder sb = new StringBuilder(); sb.append("JMBE audio conversion library ["); @@ -144,7 +192,7 @@ public class P25AudioModule extends Module implements Listener, IAudioP } else { - if (!mLibraryLoadStatusLogged) + if(!mLibraryLoadStatusLogged) { mLog.info("JMBE audio conversion library NOT FOUND"); mLibraryLoadStatusLogged = true; @@ -156,28 +204,28 @@ public class P25AudioModule extends Module implements Listener, IAudioP mLog.warn("JMBE library version 0.3.3 or higher is required - found: " + library.getVersion()); } } - catch (ClassNotFoundException e1) + catch(ClassNotFoundException e1) { - if (!mLibraryLoadStatusLogged) + if(!mLibraryLoadStatusLogged) { mLog.error("Couldn't find/load JMBE audio conversion library"); mLibraryLoadStatusLogged = true; } } - catch (InstantiationException e1) + catch(InstantiationException e1) { - if (!mLibraryLoadStatusLogged) + if(!mLibraryLoadStatusLogged) { mLog.error("Couldn't instantiate JMBE audio conversion library class"); mLibraryLoadStatusLogged = true; } } - catch (IllegalAccessException e1) + catch(IllegalAccessException e1) { - if (!mLibraryLoadStatusLogged) + if(!mLibraryLoadStatusLogged) { mLog.error("Couldn't load JMBE audio conversion library due to " - + "security restrictions"); + + "security restrictions"); mLibraryLoadStatusLogged = true; } } @@ -196,19 +244,24 @@ public class P25AudioModule extends Module implements Listener, IAudioP } /** - * Wrapper for squelch state listener. Internally, the P25 audio module - * doesn't have a squelch state. If there are IMBE audio frames, we have - * audio. We use this listener to signal to the recorder manager that the - * channel state is resetting and it should end the recording. + * Wrapper for squelch state to process end of call actions. At call end the encrypted call state established + * flag is reset so that the encrypted audio state for the next call can be properly detected and we send an + * END audio packet so that downstream processors like the audio recorder can properly close out a call sequence. */ public class SquelchStateListener implements Listener { @Override public void receive(SquelchState state) { - if (state == SquelchState.SQUELCH && mAudioPacketListener != null) + if(state == SquelchState.SQUELCH) { - mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); + if(mAudioPacketListener != null) + { + mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); + } + + mEncryptedCallStateEstablished = false; + mEncryptedCall = false; } } } diff --git a/src/module/decode/p25/message/hdu/HDUMessage.java b/src/module/decode/p25/message/hdu/HDUMessage.java index 2e7d5da3..eb3ab336 100644 --- a/src/module/decode/p25/message/hdu/HDUMessage.java +++ b/src/module/decode/p25/message/hdu/HDUMessage.java @@ -245,7 +245,7 @@ public class HDUMessage extends P25Message return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); } - public boolean isEncrypted() + public boolean isEncryptedAudio() { return getEncryption() != Encryption.UNENCRYPTED; } diff --git a/src/module/decode/p25/message/ldu/LDU1Message.java b/src/module/decode/p25/message/ldu/LDU1Message.java index 929bac1e..03f53741 100644 --- a/src/module/decode/p25/message/ldu/LDU1Message.java +++ b/src/module/decode/p25/message/ldu/LDU1Message.java @@ -1,244 +1,246 @@ package module.decode.p25.message.ldu; -import module.decode.p25.reference.DataUnitID; -import module.decode.p25.reference.LinkControlOpcode; -import module.decode.p25.reference.Vendor; -import module.decode.p25.reference.VendorLinkControlOpcode; - -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; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Vendor; +import module.decode.p25.reference.VendorLinkControlOpcode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LDU1Message extends LDUMessage { - private final static Logger mLog = LoggerFactory.getLogger( LDU1Message.class ); - - public static final int ENCRYPTION_FLAG = 352; - public static final int IMPLICIT_VENDOR_FLAG = 353; - public static final int[] OPCODE = { 354,355,356,357,362,363 }; - public static final int[] VENDOR = { 364,365,366,367,372,373,374,375 }; - - 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 }; + private final static Logger mLog = LoggerFactory.getLogger(LDU1Message.class); - 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 }; + public static final int ENCRYPTED_LINK_CONTROL_WORD_FLAG = 352; + public static final int IMPLICIT_VENDOR_FLAG = 353; + public static final int[] OPCODE = {354, 355, 356, 357, 362, 363}; + public static final int[] VENDOR = {364, 365, 366, 367, 372, 373, 374, 375}; - /* 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 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 LDU1Message( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) + 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) { - super( message, duid, 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(); - } - - /** - * Subclass constructor from an existing LDU1Message - */ - protected LDU1Message( LDU1Message message ) - { - super( message.getSourceMessage(), DataUnitID.LDU1, message.getAliasList() ); - - mCRC = message.mCRC; - } + * means it passed the CRC */ + mCRC = new CRC[3]; + mCRC[0] = CRC.PASSED; - @Override - public boolean isEncrypted() - { - return mMessage.get( ENCRYPTION_FLAG ); - } - - private void checkCRC() - { - mCRC[ 1 ] = CRC.PASSED; + checkCRC(); + } + + /** + * Subclass constructor from an existing LDU1Message + */ + protected LDU1Message(LDU1Message message) + { + super(message.getSourceMessage(), DataUnitID.LDU1, message.getAliasList()); + + mCRC = message.mCRC; + } + + /** + * Indicates if the contents of the Link Control Word are encrypted. + * + * @return true if the contents of the LCW are encrypted. + */ + public boolean isEncryptedLinkControlWord() + { + return mMessage.get(ENCRYPTED_LINK_CONTROL_WORD_FLAG); + } + + 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; - } - } + 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 ); + 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 ); + boolean irrecoverableErrors = mReedSolomonDecoder.decode(input, output); - if( irrecoverableErrors ) + if(irrecoverableErrors) { - mCRC[ 2 ] = CRC.FAILED_CRC; + 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; + 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 ); + 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(); - - sb.append( getMessageStub() ); - sb.append( " " ); - sb.append( mMessage.toString() ); - - return sb.toString(); - } - - public String getMessageStub() - { - StringBuilder sb = new StringBuilder(); - - sb.append( super.getMessageStub() ); - - if( isEncrypted() ) - { - sb.append( "ENCRYPTED" ); - } - else - { - if( isImplicitFormat() || getVendor() == Vendor.STANDARD ) - { - sb.append( getOpcode().getLabel() ); - } - else - { - sb.append( "VENDOR:" + getVendor().getLabel() + " " ); - sb.append( getVendorOpcode().getLabel() ); - } - } - - return sb.toString(); } - public boolean isImplicitFormat() - { - return mMessage.get( IMPLICIT_VENDOR_FLAG ); - } - - public LinkControlOpcode getOpcode() - { - return LinkControlOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } + 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; + } + } - public VendorLinkControlOpcode getVendorOpcode() - { - return VendorLinkControlOpcode.fromValue( mMessage.getInt( OPCODE ) ); - } - - public Vendor getVendor() - { - if( isImplicitFormat() ) - { - return Vendor.STANDARD; - } - - return Vendor.fromValue( mMessage.getInt( VENDOR ) ); - } + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + sb.append(" "); + sb.append(mMessage.toString()); + + return sb.toString(); + } + + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + + sb.append(super.getMessageStub()); + + if(isEncryptedLinkControlWord()) + { + sb.append("ENCRYPTED"); + } + else + { + if(isImplicitFormat() || getVendor() == Vendor.STANDARD) + { + sb.append(getOpcode().getLabel()); + } + else + { + sb.append("VENDOR:" + getVendor().getLabel() + " "); + sb.append(getVendorOpcode().getLabel()); + } + } + + return sb.toString(); + } + + public boolean isImplicitFormat() + { + return mMessage.get(IMPLICIT_VENDOR_FLAG); + } + + public LinkControlOpcode getOpcode() + { + return LinkControlOpcode.fromValue(mMessage.getInt(OPCODE)); + } + + public VendorLinkControlOpcode getVendorOpcode() + { + return VendorLinkControlOpcode.fromValue(mMessage.getInt(OPCODE)); + } + + public Vendor getVendor() + { + if(isImplicitFormat()) + { + return Vendor.STANDARD; + } + + return Vendor.fromValue(mMessage.getInt(VENDOR)); + } } diff --git a/src/module/decode/p25/message/ldu/LDU2Message.java b/src/module/decode/p25/message/ldu/LDU2Message.java index 8a4f196a..5fffa1dd 100644 --- a/src/module/decode/p25/message/ldu/LDU2Message.java +++ b/src/module/decode/p25/message/ldu/LDU2Message.java @@ -72,9 +72,11 @@ public class LDU2Message extends LDUMessage checkCRC(); } - - @Override - public boolean isEncrypted() + + /** + * Indicates if the audio stream is encrypted + */ + public boolean isEncryptedAudio() { Encryption encryption = getEncryption(); diff --git a/src/module/decode/p25/message/ldu/LDUMessage.java b/src/module/decode/p25/message/ldu/LDUMessage.java index 2d67e4f8..6433afd0 100644 --- a/src/module/decode/p25/message/ldu/LDUMessage.java +++ b/src/module/decode/p25/message/ldu/LDUMessage.java @@ -35,8 +35,6 @@ public abstract class LDUMessage extends P25Message super( message, duid, aliasList ); } - public abstract boolean isEncrypted(); - @Override public String getMessage() {