diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java index b6adfd90..48525a82 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2020 Dennis Sheirer, Zhenyu Mao - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** */ package io.github.dsheirer.module.decode.dmr; @@ -40,6 +37,7 @@ import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; import io.github.dsheirer.module.decode.dmr.event.DMRDecodeEvent; +import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup; import io.github.dsheirer.module.decode.dmr.message.DMRMessage; import io.github.dsheirer.module.decode.dmr.message.data.DataMessage; import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage; @@ -307,7 +305,17 @@ public class DMRDecoderState extends TimeslotDecoderState */ private void processVoice(VoiceMessage message) { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot())); + if(message.getSyncPattern().isDirectMode()) + { + updateCurrentCall(DecodeEventType.CALL, "DIRECT MODE", message.getTimestamp()); + + //Use the timeslot as the talkgroup identifier since DCDM mode doesn't use talkgroups + getIdentifierCollection().update(DMRTalkgroup.create(getTimeslot())); + } + else + { + broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot())); + } } /** diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRSyncPattern.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRSyncPattern.java index 60d94792..79a7ff10 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRSyncPattern.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRSyncPattern.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2020 Dennis Sheirer, Zhenyu Mao + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * ***************************************************************************** + * **************************************************************************** */ package io.github.dsheirer.module.decode.dmr; @@ -34,10 +33,10 @@ public enum DMRSyncPattern MOBILE_STATION_DATA(0xD5D7F77FD757l, 0xBFBEAEEABEFEl, 0x404151154101l, 0x2A28088028A8l, "MS DATA"), MOBILE_STATION_VOICE(0x7F7D5DD57DFDl, 0xEAEBFBBFEBABl, 0x151404401454l, 0x8082A22A8202l, "MS VOICE A"), - DIRECT_MODE_DATA_TIMESLOT_1(0xF7FDD5DDFD55l, 0xAEABBFBBABFFl, 0x515440445400l, 0x08022A2202AAl, "DM DAT0"), - DIRECT_MODE_DATA_TIMESLOT_2(0xD7557F5FF7F5l, 0xBEFFEAFAAEAFl, 0x410015055150l, 0x28AA80A0080Al, "DM DAT1"), - DIRECT_MODE_VOICE_TIMESLOT_1(0x5D577F7757FFl, 0xFBFEEAEEFEAAl, 0x040115110155l, 0xA2A88088A800l, "DM VOX0"), - DIRECT_MODE_VOICE_TIMESLOT_2(0x7DFFD5F55D5Fl, 0xEBAABFAFFBFAl, 0x145540500405l, 0x82002A0AA2A0l, "DM VOX1"), + DIRECT_MODE_DATA_TIMESLOT_1(0xF7FDD5DDFD55l, 0xAEABBFBBABFFl, 0x515440445400l, 0x08022A2202AAl, "MS DATA - DUAL CAPACITY DIRECT MODE"), + DIRECT_MODE_DATA_TIMESLOT_2(0xD7557F5FF7F5l, 0xBEFFEAFAAEAFl, 0x410015055150l, 0x28AA80A0080Al, "MS DATA - DUAL CAPACITY DIRECT MODE"), + DIRECT_MODE_VOICE_TIMESLOT_1(0x5D577F7757FFl, 0xFBFEEAEEFEAAl, 0x040115110155l, 0xA2A88088A800l, "MS VOICE A - DUAL CAPACITY DIRECT MODE"), + DIRECT_MODE_VOICE_TIMESLOT_2(0x7DFFD5F55D5Fl, 0xEBAABFAFFBFAl, 0x145540500405l, 0x82002A0AA2A0l, "MS VOICE A - DUAL CAPACITY DIRECT MODE"), MOBILE_STATION_REVERSE_CHANNEL(0x77D55F7DFD77l, 0xEEBFFAEBABEEl, 0x114005145411l, 0x882AA0820288l, "MS RVRS"), RESERVED(0xDD7FF5D757DDl, 0xBBEAAFBEFEBBl, 0x441550410144l, 0x22800A28A822l, "RESERVED"), diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java b/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java index 1007554d..fdc3fa78 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -102,11 +102,22 @@ public class DMRAudioModule extends AmbeAudioModule implements IdentifierUpdateP { if(hasAudioCodec() && message.getTimeslot() == getTimeslot()) { + //DCDM doesn't provide FLCs or EMBs ... assume that the call is unencrypted. + if(!mEncryptedCallStateEstablished && message instanceof VoiceMessage vm && + vm.getSyncPattern().isDirectMode()) + { + mEncryptedCallStateEstablished = true; + mEncryptedCall = false; + List frames = vm.getAMBEFrames(); + for(byte[] frame: frames) + { + processAudio(frame); + } + } //Both Motorola and Hytera signal their Basic Privacy (BP) scrambling in some of the Voice B-F frames //in the EMB field. - if(!mEncryptedCallStateEstablished && message instanceof VoiceEMBMessage) + else if(!mEncryptedCallStateEstablished && message instanceof VoiceEMBMessage voiceMessage) { - VoiceEMBMessage voiceMessage = (VoiceEMBMessage)message; if(voiceMessage.getEMB().isValid()) { mEncryptedCallStateEstablished = true; @@ -119,23 +130,20 @@ public class DMRAudioModule extends AmbeAudioModule implements IdentifierUpdateP processAudio(frame); } } - else if(message instanceof VoiceMessage) + else if(message instanceof VoiceMessage voiceMessage) { - VoiceMessage voiceMessage = (VoiceMessage)message; List frames = voiceMessage.getAMBEFrames(); for(byte[] frame: frames) { processAudio(frame); } } - else if(!mEncryptedCallStateEstablished && message instanceof VoiceHeader) + else if(!mEncryptedCallStateEstablished && message instanceof VoiceHeader voiceHeader) { - LCMessage lc = ((VoiceHeader)message).getLCMessage(); + LCMessage lc = voiceHeader.getLCMessage(); - if(lc instanceof FullLCMessage) //We know it is, but this null checks as well + if(lc instanceof FullLCMessage flc) //We know it is, but this null checks as well { - FullLCMessage flc = (FullLCMessage)lc; - if(flc.isValid()) { mEncryptedCallStateEstablished = true; @@ -146,9 +154,9 @@ public class DMRAudioModule extends AmbeAudioModule implements IdentifierUpdateP //Note: the DMRMessageProcess extracts Full Link Control messages from Voice Frames B-C and sends them // independent of any DMR Burst messaging. When encountered, it can be assumed that they are part of // an ongoing call and can be used to establish encryption state when the FLC is a voice channel user. - else if(!mEncryptedCallStateEstablished && message instanceof AbstractVoiceChannelUser) + else if(!mEncryptedCallStateEstablished && message instanceof AbstractVoiceChannelUser avcu) { - ServiceOptions serviceOptions = ((AbstractVoiceChannelUser)message).getServiceOptions(); + ServiceOptions serviceOptions = avcu.getServiceOptions(); mEncryptedCallStateEstablished = true; mEncryptedCall = serviceOptions.isEncrypted(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRMessageFactory.java index cfd84c77..96d04b0e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRMessageFactory.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message; import io.github.dsheirer.bits.CorrectedBinaryMessage; @@ -33,6 +52,12 @@ public class DMRMessageFactory case BS_VOICE_FRAME_D: case BS_VOICE_FRAME_E: case BS_VOICE_FRAME_F: + + case MS_VOICE_FRAME_B: + case MS_VOICE_FRAME_C: + case MS_VOICE_FRAME_D: + case MS_VOICE_FRAME_E: + case MS_VOICE_FRAME_F: return createVoiceMessage(syncPattern, binaryMessage, cach, timestamp, timeslot); case DIRECT_MODE_VOICE_TIMESLOT_1: return createVoiceMessage(syncPattern, binaryMessage, cach, timestamp, 1); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceAMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceAMessage.java index 45188ce6..2f77ec6a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceAMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceAMessage.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message.voice; import io.github.dsheirer.bits.CorrectedBinaryMessage; @@ -29,7 +48,16 @@ public class VoiceAMessage extends VoiceMessage public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(" ").append(getSyncPattern()); + + if(getSyncPattern().isDirectMode()) + { + sb.append(getSyncPattern()); + } + else + { + sb.append(" ").append(getSyncPattern()); + } + return sb.toString(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceEMBMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceEMBMessage.java index b6e8387a..aebe3c07 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceEMBMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceEMBMessage.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + package io.github.dsheirer.module.decode.dmr.message.voice; import io.github.dsheirer.bits.BinaryMessage; @@ -37,25 +56,14 @@ public class VoiceEMBMessage extends VoiceMessage if(getEMB().isValid()) { - sb.append("CC:").append(getEMB().getColorCode()); - } - else - { - sb.append("CC:?"); + sb.append("CC:").append(getEMB().getColorCode()).append(" "); } - sb.append(" ").append(getSyncPattern().toString()); + sb.append(getSyncPattern().toString()); - if(getEMB().isValid()) + if(getEMB().isValid() && getEMB().isEncrypted()) { - if(getEMB().isEncrypted()) - { - sb.append(" ENCRYPTED"); - } - } - else - { - sb.append(" [EMB CRC-ERROR]"); + sb.append(" ENCRYPTED"); } return sb.toString();