From 2ffb2963afde6ca8c379de4361fbdeafb5e8d0f4 Mon Sep 17 00:00:00 2001 From: sheirerd Date: Sun, 30 Jul 2023 08:28:28 -0400 Subject: [PATCH] #1618 Update Cap+ radio & group parsing, process TXI & voice frame encryption parameters, and many other changes to the DMR message processing. --- .idea/misc.xml | 2 +- .../io/github/dsheirer/edac/BPTC_16_2.java | 125 ++++++++ .../java/io/github/dsheirer/edac/CRCUtil.java | 28 +- .../java/io/github/dsheirer/edac/Golay24.java | 53 ++-- .../dsheirer/gui/dmr/DMRRecordingViewer.java | 6 + .../module/decode/dmr/DMRDecoderState.java | 26 +- .../decode/dmr/DMRMessageProcessor.java | 40 ++- .../dmr/DMRNetworkConfigurationMonitor.java | 2 +- .../decode/dmr/DMRTrafficChannelManager.java | 2 +- .../dmr/audio/DMRCallSequenceRecorder.java | 9 +- .../dmr/channel/DMRAbsoluteChannel.java | 29 +- .../module/decode/dmr/channel/DMRChannel.java | 43 +-- .../{DMRLogicalChannel.java => DMRLsn.java} | 92 ++++-- .../decode/dmr/channel/DMRTier3Channel.java | 73 ++++- .../module/decode/dmr/channel/DmrRestLsn.java | 42 +++ .../channel/ITimeslotFrequencyReceiver.java | 4 +- .../decode/dmr/channel/TimeslotFrequency.java | 8 +- .../module/decode/dmr/message/DMRBurst.java | 29 ++ .../dmr/message/IServiceOptionsProvider.java | 34 +++ .../message/data/DMRDataMessageFactory.java | 6 +- .../csbk/motorola/CapacityPlusNeighbors.java | 29 +- .../csbk/motorola/CapacityPlusSiteStatus.java | 38 +-- .../motorola/ConnectPlusDataChannelGrant.java | 30 +- .../motorola/ConnectPlusOTAAnnouncement.java | 28 +- .../motorola/ConnectPlusVoiceChannelUser.java | 30 +- .../dmr/message/data/csbk/standard/Clear.java | 14 +- .../announcement/AdjacentSiteInformation.java | 43 +-- .../announcement/AnnounceWithdrawTSCC.java | 3 +- .../standard/announcement/VoteNowAdvice.java | 17 +- .../csbk/standard/grant/ChannelGrant.java | 17 +- .../data/header/DefinedShortDataHeader.java | 4 +- .../data/header/ProprietaryDataHeader.java | 17 +- .../motorola/MNISProprietaryDataHeader.java | 5 +- ...java => MotorolaDataEncryptionHeader.java} | 58 +++- .../dmr/message/data/lc/LCMessageFactory.java | 9 +- .../decode/dmr/message/data/lc/LCOpcode.java | 2 + .../lc/full/AbstractVoiceChannelUser.java | 5 +- .../data/lc/full/hytera/HyteraFullLC.java | 5 +- ...CapacityPlusEncryptedVoiceChannelUser.java | 6 +- .../CapacityPlusGroupVoiceChannelUser.java | 236 --------------- .../CapacityPlusVoiceChannelUser.java | 4 +- .../CapacityPlusWideAreaVoiceChannelUser.java | 73 ++--- .../MotorolaEncryptionParameters.java | 128 ++++++++ .../MotorolaGroupVoiceChannelUser.java | 122 ++++++++ .../data/lc/shorty/ActivityUpdateMessage.java | 18 +- .../lc/shorty/CapacityPlusRestChannel.java | 31 +- .../message/data/lc/shorty/NullMessage.java | 6 +- .../message/data/packet/DMRPacketMessage.java | 7 +- .../message/data/packet/PacketSequence.java | 10 + .../packet/PacketSequenceMessageFactory.java | 19 +- .../type/CapacityPlusServiceOptions.java | 2 +- .../module/decode/dmr/message/voice/EMB.java | 6 +- .../dmr/message/voice/VoiceEMBMessage.java | 40 ++- .../dmr/message/voice/VoiceMessage.java | 46 ++- .../voice/VoiceSuperFrameProcessor.java | 282 ++++++++++++++++++ .../embedded/Arc4EncryptionParameters.java | 80 +++++ .../voice/embedded/EmbeddedParameters.java | 90 ++++++ .../voice/embedded/NonStandardShortBurst.java | 44 +++ .../voice/embedded/NullShortBurst.java | 45 +++ .../message/voice/embedded/ShortBurst.java | 134 +++++++++ .../voice/embedded/ShortBurstOpcode.java | 71 +++++ .../voice/embedded/TransmitInterrupt.java | 74 +++++ .../voice/embedded/UnknownShortBurst.java | 44 +++ .../decode/ip/DefinedShortDataPacket.java | 98 ++++++ 64 files changed, 2039 insertions(+), 684 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/edac/BPTC_16_2.java rename src/main/java/io/github/dsheirer/module/decode/dmr/channel/{DMRLogicalChannel.java => DMRLsn.java} (51%) create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/channel/DmrRestLsn.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/IServiceOptionsProvider.java rename src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/{MotorolaProprietaryDataHeader.java => MotorolaDataEncryptionHeader.java} (62%) delete mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusGroupVoiceChannelUser.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaEncryptionParameters.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaGroupVoiceChannelUser.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceSuperFrameProcessor.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/Arc4EncryptionParameters.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/EmbeddedParameters.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NonStandardShortBurst.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NullShortBurst.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurst.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurstOpcode.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/TransmitInterrupt.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/UnknownShortBurst.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/DefinedShortDataPacket.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 29ce38d1d..00f52ed7b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/edac/BPTC_16_2.java b/src/main/java/io/github/dsheirer/edac/BPTC_16_2.java new file mode 100644 index 000000000..9840e1c63 --- /dev/null +++ b/src/main/java/io/github/dsheirer/edac/BPTC_16_2.java @@ -0,0 +1,125 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.edac; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Block Product Turbo Code 16/2 for decoding a DMR Voice Frame F payload from the 32-bit EMB field. + *

+ * See ETSI TS 102 361-1 B.2.2.1 and B.2.2.2 + */ +public class BPTC_16_2 +{ + private static final int[] DEINTERLEAVE = new int[]{0, 24, 1, 25, 2, 26, 3, 27, 4, 28, 5, 29, 6, 30, 7, 31, 8, 16, + 9, 17, 10, 18, 11, 19, 12, 20, 13, 21, 14, 22, 15, 23}; + + /** + * Unscramble and perform FEC checks per paragraph B.2.2.1 for Non-Reverse Channel Single Burst + * + * @param message with 32 interleaved bits. + * @return descrambled and error checked message or null if the process fails or there are too many errors. + */ + public static CorrectedBinaryMessage decodeShortBurst(CorrectedBinaryMessage message) + { + CorrectedBinaryMessage deinterleaved = deinterleave(message); + int fec = Hamming16.checkAndCorrect(deinterleaved, 0); + + if(fec == 2) //0 or 1 is good, 2 = uncorrectable errors + { + return null; + } + + //Check for even parity. Bits 0-15 should be the same as bits 16-31. + for(int x = 0; x < 16; x++) + { + if(deinterleaved.get(x) ^ deinterleaved.get(x + 16)) + { + return null; + } + } + + return deinterleaved; + } + + /** + * Unscramble and perform FEC checks per paragraph B.2.2.2 for Reverse Channel Single Burst + * + * @param binaryMessage with 32 interleaved bits. + * @return descrambled and error checked message or null if the process fails or there are too many errors. + */ + public static CorrectedBinaryMessage decodeReverseChannel(CorrectedBinaryMessage message) + { + CorrectedBinaryMessage deinterleaved = deinterleave(message); + System.out.println(" DEINTER: " + deinterleaved.toHexString()); + int fec = Hamming16.checkAndCorrect(deinterleaved, 0); + System.out.println(" DECODED: " + deinterleaved.toHexString()); + System.out.println("FEC:" + fec); + if(fec == 2) //0 or 1 is good, 2 = uncorrectable errors + { + return null; + } + + //Check for odd parity. Bits 0-15 should be opposite of bits 16-31. + for(int x = 0; x < 16; x++) + { + if(deinterleaved.get(x) == deinterleaved.get(x + 16)) + { + return null; + } + } + + return deinterleaved; + } + + /** + * Performs deinterleave of the interleaved message. + * + * @param original to deinterleave + * @return deinterleaved message + */ + public static CorrectedBinaryMessage deinterleave(CorrectedBinaryMessage original) + { + CorrectedBinaryMessage delinterleaved = new CorrectedBinaryMessage(32); + for(int x = 0; x < 32; x++) + { + if(original.get(x)) + { + delinterleaved.set(DEINTERLEAVE[x]); + } + } + + return delinterleaved; + } + + public static void main(String[] args) + { + String[] msgs = new String[]{"05030A03", "35003A00", "1C6D2C9E"}; + + for(String msg : msgs) + { + CorrectedBinaryMessage original = new CorrectedBinaryMessage(BinaryMessage.loadHex(msg)); + System.out.println("ORIGINAL: " + original.toHexString()); + CorrectedBinaryMessage decoded = decodeReverseChannel(original); + System.out.println("-------------------------"); + } + } +} diff --git a/src/main/java/io/github/dsheirer/edac/CRCUtil.java b/src/main/java/io/github/dsheirer/edac/CRCUtil.java index 4b0b08a2a..be7382df4 100644 --- a/src/main/java/io/github/dsheirer/edac/CRCUtil.java +++ b/src/main/java/io/github/dsheirer/edac/CRCUtil.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -226,17 +226,19 @@ public static void main(String[] args) { mLog.debug("Starting"); - //DMR message -// String raw = "100110010001000000001001000000001111011011010000011001010000000000000000000000001100001000001011"; - String raw = "101010000000000000110110100000101101011011010000011001111100000000000011100101001001111010001110"; - mLog.debug(raw); - raw = "101010000000000000110110100000101101011011010000011001111100000000000011100101000000000000000000"; - BinaryMessage message = BinaryMessage.load(raw); - mLog.debug(message.toString()); - - long polynomial = 0x11021l; - decode(message, 0, 80, polynomial, 16); - mLog.debug(message.toString()); - mLog.debug("Finished"); + long poly = 0x13l; + long[] checksums = generate(32, 4, poly, 0, true); + + StringBuilder sb = new StringBuilder(); + sb.append("private static int[] CHECKSUMS = new int[]{"); + for(long checksum: checksums) + { + sb.append("0x").append(Long.toHexString(checksum).toUpperCase()); + sb.append(","); + } + + sb.append("};"); + + System.out.println("Checksums:\n" + sb); } } diff --git a/src/main/java/io/github/dsheirer/edac/Golay24.java b/src/main/java/io/github/dsheirer/edac/Golay24.java index 44ad8a504..b6735d961 100644 --- a/src/main/java/io/github/dsheirer/edac/Golay24.java +++ b/src/main/java/io/github/dsheirer/edac/Golay24.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.edac; import io.github.dsheirer.bits.BinaryMessage; @@ -5,27 +24,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - * ----------------------------------------------------------------------- - * Galois24 decoder based on Hank Wallace's tutorial/algorithm located at: - * http://www.aqdi.com/golay.htm - ******************************************************************************/ - /** * Galois 24/12/7 decoder */ @@ -160,4 +158,17 @@ private static int getSyndrome(BinaryMessage message, int startIndex) return (checksum ^ calculated); } + + public static void main(String[] args) + { +// CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("F3BB20")); +// CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("F0C5C0")); + CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("AFAC00")); + + System.out.println("M:" + bm.toHexString()); + int a = Golay24.checkAndCorrect(bm, 0); + System.out.println("M:" + bm.toHexString()); + + System.out.println("A:" + a); + } } diff --git a/src/main/java/io/github/dsheirer/gui/dmr/DMRRecordingViewer.java b/src/main/java/io/github/dsheirer/gui/dmr/DMRRecordingViewer.java index f177750fe..0fd1adcbd 100644 --- a/src/main/java/io/github/dsheirer/gui/dmr/DMRRecordingViewer.java +++ b/src/main/java/io/github/dsheirer/gui/dmr/DMRRecordingViewer.java @@ -459,6 +459,12 @@ private TextField getFindText() if(mFindText == null) { mFindText = new TextField(); + mFindText.setOnKeyPressed(event -> { + if(event.getCode().equals(KeyCode.ENTER)) + { + getFindButton().fire(); + } + }); mFindText.textProperty().addListener((observable, oldValue, newValue) -> updateFilters()); } 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 4bfd1b65a..df8c035e8 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 @@ -65,8 +65,8 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraUnitToUnitVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusEncryptedVoiceChannelUser; -import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusWideAreaVoiceChannelUser; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.CapacityPlusRestChannel; import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage; import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService; @@ -646,12 +646,12 @@ private void processCSBK(CSBKMessage csbk) } else { - DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber()); + DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue()); if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) { event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers); - mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); + mDetectedCallEventsMap.put(channel.getValue(), event); } else { @@ -679,12 +679,12 @@ private void processCSBK(CSBKMessage csbk) } else { - DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber()); + DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue()); if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) { event = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers); - mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); + mDetectedCallEventsMap.put(channel.getValue(), event); } else { @@ -711,12 +711,12 @@ private void processCSBK(CSBKMessage csbk) } else { - DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber()); + DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue()); if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) { event = getDecodeEvent(csbk, DecodeEventType.CALL_UNIT_TO_UNIT, channel, mergedIdentifiers); - mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); + mDetectedCallEventsMap.put(channel.getValue(), event); } else { @@ -756,12 +756,12 @@ private void processCSBK(CSBKMessage csbk) } else { - DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber()); + DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue()); if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers())) { event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers); - mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event); + mDetectedCallEventsMap.put(channel.getValue(), event); } else { @@ -807,12 +807,12 @@ private void processCSBK(CSBKMessage csbk) } else { - DecodeEvent detectedEvent = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber()); + DecodeEvent detectedEvent = mDetectedCallEventsMap.get(channel.getValue()); if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers())) { detectedEvent = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers); - mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent); + mDetectedCallEventsMap.put(channel.getValue(), detectedEvent); } else { @@ -979,10 +979,8 @@ private void processLinkControl(LCMessage message, boolean isTerminator) } break; case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER: - if(message instanceof CapacityPlusGroupVoiceChannelUser cpgvcu) + if(message instanceof MotorolaGroupVoiceChannelUser cpgvcu) { - updateRestChannel(cpgvcu.getRestChannel()); - if(isTerminator) { getIdentifierCollection().remove(Role.FROM); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageProcessor.java index 0ac7d2271..97a9f3672 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageProcessor.java @@ -44,6 +44,8 @@ import io.github.dsheirer.module.decode.dmr.message.data.packet.PacketSequenceAssembler; import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator; import io.github.dsheirer.module.decode.dmr.message.voice.VoiceEMBMessage; +import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage; +import io.github.dsheirer.module.decode.dmr.message.voice.VoiceSuperFrameProcessor; import io.github.dsheirer.sample.Listener; import java.util.ArrayList; import java.util.List; @@ -59,6 +61,8 @@ public class DMRMessageProcessor implements Listener { private final static Logger mLog = LoggerFactory.getLogger(DMRMessageProcessor.class); private DecodeConfigDMR mConfigDMR; + private VoiceSuperFrameProcessor mSuperFrameProcessor1 = new VoiceSuperFrameProcessor(); + private VoiceSuperFrameProcessor mSuperFrameProcessor2 = new VoiceSuperFrameProcessor(); private FLCAssembler mFLCAssemblerTimeslot1 = new FLCAssembler(1); private FLCAssembler mFLCAssemblerTimeslot2 = new FLCAssembler(2); private MBCAssembler mMBCAssembler = new MBCAssembler(); @@ -89,11 +93,45 @@ public DMRMessageProcessor(DecodeConfigDMR config) @Override public void receive(IMessage message) { + if(message instanceof FullLCMessage flc) + { + if(flc.getTimeslot() == 1) + { + mSuperFrameProcessor1.process(flc); + } + else + { + mSuperFrameProcessor2.process(flc); + } + } + else if(message instanceof VoiceMessage voiceMessage) + { + if(voiceMessage.getTimeslot() == 1) + { + mSuperFrameProcessor1.process(voiceMessage); + } + else + { + mSuperFrameProcessor2.process(voiceMessage); + } + } + else if(message instanceof DMRBurst dmrBurst && dmrBurst.isValid()) + { + if(dmrBurst.getTimeslot() == 1) + { + mSuperFrameProcessor1.reset(); + } + else + { + mSuperFrameProcessor2.reset(); + } + } + //Enrich messages that carry DMR Logical Slot Number channels with LCN to frequency mappings if(message instanceof ITimeslotFrequencyReceiver) { ITimeslotFrequencyReceiver receiver = (ITimeslotFrequencyReceiver)message; - int[] lsns = receiver.getLogicalTimeslotNumbers(); + int[] lsns = receiver.getLogicalSlotNumbers(); List timeslotFrequencies = new ArrayList<>(); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java index a278f6ecb..857d46f7f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java @@ -279,7 +279,7 @@ public void process(CSBKMessage csbk) */ private void addDmrChannel(DMRChannel dmrChannel) { - mObservedChannelMap.put(dmrChannel.getLogicalSlotNumber(), dmrChannel); + mObservedChannelMap.put(dmrChannel.getValue(), dmrChannel); } public void reset() diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java index eea406ff8..3b64a8e8a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java @@ -322,7 +322,7 @@ public void processEndChannelGrant() public void processChannelGrant(DMRChannel channel, IdentifierCollection identifierCollection, Opcode opcode, long timestamp, boolean encrypted) { - int lsn = channel.getLogicalSlotNumber(); + int lsn = channel.getValue(); DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn); DecodeEventType decodeEventType = getEventType(opcode, identifierCollection, encrypted); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRCallSequenceRecorder.java b/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRCallSequenceRecorder.java index 676001d29..fa38204f6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRCallSequenceRecorder.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRCallSequenceRecorder.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -32,17 +32,16 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.full.UnitToUnitVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraUnitToUnitVoiceChannelUser; -import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusWideAreaVoiceChannelUser; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator; import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.sample.Listener; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; - /** * DMR AMBE Frame recorder generates call sequence recordings containing JSON representations of audio * frames, optional encryption and call identifiers. @@ -207,7 +206,7 @@ private void process(FullLCMessage message) switch(message.getOpcode()) { case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER: - if(message instanceof CapacityPlusGroupVoiceChannelUser cpvcu) + if(message instanceof MotorolaGroupVoiceChannelUser cpvcu) { if(mCallSequence == null) { diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRAbsoluteChannel.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRAbsoluteChannel.java index 93b48d7da..bf773bf1b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRAbsoluteChannel.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRAbsoluteChannel.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 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 - * * ***************************************************************************** + * 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.channel; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRChannel.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRChannel.java index bdfc361d8..e4653a2ca 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRChannel.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRChannel.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -56,9 +56,9 @@ public Protocol getProtocol() } /** - * Repeater number for the channel. + * Repeater number or channel number for the channel. */ - public int getRepeater() + public int getChannel() { return getValue(); } @@ -72,36 +72,6 @@ public int getTimeslot() return mTimeslot; } - /** - * Logical slot number for this channel. LSN is a 1-based index value where repeater one, timeslot 1 is - * LSN 1, timeslot 2 is LSN 2, etc. - * - * Formula: LSN = ((channel - 1) * 2) + timeslot - * - * @return logical slot number, a 1-based index value - */ - public int getLogicalSlotNumber() - { - int repeater = getRepeater(); - - if(repeater > 0) - { - return ((repeater - 1) * 2) + getTimeslot(); - } - - return 0; - } - - /** - * Returns an array of length 1 containing this channel's logical slot number - */ - public int[] getLSNArray() - { - int[] logicalSlotNumbers = new int[1]; - logicalSlotNumbers[0] = getLogicalSlotNumber(); - return logicalSlotNumbers; - } - /** * Number of timeslots for the DMR channel. * @return 2 always. @@ -121,6 +91,13 @@ public boolean isTDMAChannel() return true; } + + @Override + public String toString() + { + return "CHAN:" + getChannel() + ":" + getTimeslot(); + } + /** * Not implemented */ diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRLogicalChannel.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRLsn.java similarity index 51% rename from src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRLogicalChannel.java rename to src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRLsn.java index 07088f10f..1cb9c5a83 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRLogicalChannel.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRLsn.java @@ -19,29 +19,60 @@ package io.github.dsheirer.module.decode.dmr.channel; +import java.util.List; + /** - * DMR logical channel. This channel uses a logical channel number and a timeslot. + * DMR Logical Slot Number (LSN) channel. + * + * Note: LSN 1-16 are represented in memory using the following channel and timeslot values: + * + * LSN: CHANNEL/TIMESLOT + * 1: 1/1 + * 2: 1/2 + * 3: 2/1 + * 4: 2/2 + * 5: 3/1 + * 6: 3/2 + * 7: 4/1 + * 8: 4/2 + * 9: 5/1 + * 10: 5/2 + * 11: 6/1 + * 12: 6/2 + * 13: 7/1 + * 14: 7/2 + * 15: 8/1 + * 16: 8/2 */ -public class DMRLogicalChannel extends DMRChannel +public class DMRLsn extends DMRChannel implements ITimeslotFrequencyReceiver { private TimeslotFrequency mTimeslotFrequency; /** - * Constructs an instance. Note: radio reference uses a one based index, so we add a value of one to the - * calculated logical slot value for visual compatibility for users. + * Constructs an instance * - * @param channel number or repeater number - * @param logicalSlotNumber - zero based index. + * @param lsn in range 1 - 16 */ - public DMRLogicalChannel(int channel, int timeslot) + public DMRLsn(int lsn) + { + super(((lsn - 1) / 2) + 1, ((lsn - 1) % 2) + 1); + } + + @Override + public String toString() { - super(channel, timeslot); + return "LSN:" + getLsn(); } /** - * Downlink frequency - * @return value in Hertz, or 0 if this channel doesn't have a timeslot frequency mapping + * Logical Slot Number + * @return lsn */ + public int getLsn() + { + return ((getChannel() - 1) * 2) + getTimeslot(); + } + @Override public long getDownlinkFrequency() { @@ -53,10 +84,6 @@ public long getDownlinkFrequency() return 0; } - /** - * Uplink frequency - * @return value in Hertz, or 0 if this channel doesn't have a timeslot frequency mapping - */ @Override public long getUplinkFrequency() { @@ -68,23 +95,40 @@ public long getUplinkFrequency() return 0; } + @Override + public int[] getLogicalSlotNumbers() + { + return new int[]{getLsn()}; + } + /** - * Sets the timeslot frequency mapping - * @param timeslotFrequency + * Sets the lsn to frequency mapper value. + * @param timeslotFrequency to set */ public void setTimeslotFrequency(TimeslotFrequency timeslotFrequency) { mTimeslotFrequency = timeslotFrequency; } - /** - * Formatted channel number - */ - public String toString() + @Override + public void apply(List timeslotFrequencies) { - StringBuilder sb = new StringBuilder(); - sb.append("LSN:").append(getLogicalSlotNumber()); -// sb.append(" LCN:").append(getValue()); - return sb.toString(); + for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) + { + if(timeslotFrequency.getNumber() == getLsn()) + { + setTimeslotFrequency(timeslotFrequency); + return; + } + } + } + + public static void main(String[] args) + { + for(int x = 1; x <= 16; x++) + { + DMRLsn lsn = new DmrRestLsn(x); + System.out.println(lsn); + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java index 824b95dcb..3955bc46b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java @@ -1,16 +1,37 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.channel; /** - * DMR Tier III Trunking Channel + * DMR Tier III Trunking channel. This channel uses a logical channel number and a timeslot. */ -public class DMRTier3Channel extends DMRLogicalChannel +public class DMRTier3Channel extends DMRChannel { + private TimeslotFrequency mTimeslotFrequency; + /** * Constructs an instance. Note: radio reference uses a one based index, so we add a value of one to the * calculated logical slot value for visual compatibility for users. * * @param channel number or repeater number - * @param timeslot + * @param logicalSlotNumber - zero based index. */ public DMRTier3Channel(int channel, int timeslot) { @@ -18,23 +39,51 @@ public DMRTier3Channel(int channel, int timeslot) } /** - * Logical slot number for this channel. - * - * Formula: LSN = (channel * 2) + timeslot - * - * @return logical slot number, a 1-based index value + * Downlink frequency + * @return value in Hertz, or 0 if this channel doesn't have a timeslot frequency mapping */ @Override - public int getLogicalSlotNumber() + public long getDownlinkFrequency() { - int repeater = getRepeater(); + if(mTimeslotFrequency != null) + { + return mTimeslotFrequency.getDownlinkFrequency(); + } + + return 0; + } - if(repeater > 0) + /** + * Uplink frequency + * @return value in Hertz, or 0 if this channel doesn't have a timeslot frequency mapping + */ + @Override + public long getUplinkFrequency() + { + if(mTimeslotFrequency != null) { - return ((repeater) * 2) + getTimeslot(); + return mTimeslotFrequency.getUplinkFrequency(); } return 0; } + /** + * Sets the timeslot frequency mapping + * @param timeslotFrequency + */ + public void setTimeslotFrequency(TimeslotFrequency timeslotFrequency) + { + mTimeslotFrequency = timeslotFrequency; + } + + /** + * Formatted channel number + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(" LCN:").append(getValue()); + return sb.toString(); + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DmrRestLsn.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DmrRestLsn.java new file mode 100644 index 000000000..c29135a20 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DmrRestLsn.java @@ -0,0 +1,42 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.channel; + +/** + * DMR Rest Channel logical slot number (LSN). + */ +public class DmrRestLsn extends DMRLsn +{ + /** + * Constructs an instance + * + * @param lsn in range 1 - 16 + */ + public DmrRestLsn(int lsn) + { + super(lsn); + } + + @Override + public String toString() + { + return "REST:" + getLsn(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/ITimeslotFrequencyReceiver.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/ITimeslotFrequencyReceiver.java index a1f5b245d..fffd15418 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/ITimeslotFrequencyReceiver.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/ITimeslotFrequencyReceiver.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -26,7 +26,7 @@ public interface ITimeslotFrequencyReceiver /** * Provides the logical slot number(s) that require a matching timeslot frequency mapping */ - public int[] getLogicalTimeslotNumbers(); + public int[] getLogicalSlotNumbers(); /** * Applies the list of timeslot frequency mappings to the implementer diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/TimeslotFrequency.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/TimeslotFrequency.java index 08adf174d..1a5a4e810 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/TimeslotFrequency.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/TimeslotFrequency.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -31,7 +31,7 @@ import javafx.util.Callback; /** - * Maps a timeslot number to a pair of channel frequency values + * Maps a logical slot number (LSN) to a pair of channel frequency values */ public class TimeslotFrequency { @@ -73,7 +73,7 @@ public IntegerProperty getNumberProperty() * Downlink Frequency property */ @JsonIgnore - public LongProperty downlinkFrequencyPropertyProperty() + public LongProperty downlinkFrequencyProperty() { return mDownlinkFrequencyProperty; } @@ -168,7 +168,7 @@ public String toString() */ public static Callback extractor() { - return (TimeslotFrequency tf) -> new Observable[] {tf.getNumberProperty(), tf.downlinkFrequencyPropertyProperty(), + return (TimeslotFrequency tf) -> new Observable[] {tf.getNumberProperty(), tf.downlinkFrequencyProperty(), tf.uplinkFrequencyProperty(), tf.getDownlinkMHz(), tf.getUplinkMHz()}; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRBurst.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRBurst.java index 5a8c8f907..06db716dd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRBurst.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/DMRBurst.java @@ -1,5 +1,25 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.BinaryMessage; import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; /** @@ -53,4 +73,13 @@ public DMRSyncPattern getSyncPattern() { return mSyncPattern; } + + /** + * Extracts the sync payload from between the two payload fragments. + * @return binary message containing just the sync payload bits (64). + */ + public BinaryMessage getSyncPayload() + { + return getMessage().get(SYNC_START, PAYLOAD_2_START); + } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/IServiceOptionsProvider.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/IServiceOptionsProvider.java new file mode 100644 index 000000000..ba2ff3d4a --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/IServiceOptionsProvider.java @@ -0,0 +1,34 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.module.decode.dmr.message.type.ServiceOptions; + +/** + * DMR message that exposes a service options configuration + */ +public interface IServiceOptionsProvider +{ + /** + * Service Options + * @return service options + */ + ServiceOptions getServiceOptions(); +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DMRDataMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DMRDataMessageFactory.java index 3fd873855..0b13a0cca 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DMRDataMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/DMRDataMessageFactory.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -45,7 +45,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.header.VoiceHeader; import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraProprietaryDataHeader; import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MNISProprietaryDataHeader; -import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MotorolaProprietaryDataHeader; +import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MotorolaDataEncryptionHeader; import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock; import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator; import io.github.dsheirer.module.decode.dmr.message.data.usb.USBData; @@ -132,7 +132,7 @@ public static DataMessage create(DMRSyncPattern pattern, CorrectedBinaryMessage } else { - MotorolaProprietaryDataHeader mprdh = new MotorolaProprietaryDataHeader(pattern, + MotorolaDataEncryptionHeader mprdh = new MotorolaDataEncryptionHeader(pattern, payload, cach, slotType, timestamp, timeslot); mprdh.setValid(valid); return mprdh; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusNeighbors.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusNeighbors.java index 9f4dc5b38..409d10bad 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusNeighbors.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusNeighbors.java @@ -22,7 +22,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DmrRestLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.identifier.DMRSite; @@ -39,7 +39,6 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequencyReceiver { private static final int[] LC_START_STOP = new int[]{16, 17}; - private static final int TIMESLOT = 18; private static final int[] REST_LSN = new int[]{19, 20, 21, 22, 23}; private static final int ASYNC = 24; private static final int[] SITE = new int[]{25, 26, 27, 28}; @@ -58,7 +57,7 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequ private static final int[] NEIGHBOR_6_REST = new int[]{76, 77, 78, 79}; - private DMRLogicalChannel mRestChannel; + private DmrRestLsn mRestChannel; private DMRSite mSite; private List mIdentifiers; @@ -286,37 +285,23 @@ public int getRestLSN() return getMessage().getInt(REST_LSN); } - /** - * Rest repeater - */ - public int getRestRepeater() { return (int) Math.ceil(getRestLSN() / 2.0); } - - /** - * Rest timeslot - * @return timeslot 1 or 2 - */ - public int getRestTimeslot() - { - return (getRestLSN() % 2 == 0) ? 2 : 1; - } - /** * DMR Channel */ - public DMRLogicalChannel getRestChannel() + public DmrRestLsn getRestChannel() { if(mRestChannel == null) { - mRestChannel = new DMRLogicalChannel(getRestRepeater(), getRestTimeslot()); + mRestChannel = new DmrRestLsn(getRestLSN()); } return mRestChannel; } @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getRestChannel().getLSNArray(); + return getRestChannel().getLogicalSlotNumbers(); } /** @@ -329,7 +314,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency : timeslotFrequencies) { - if(timeslotFrequency.getNumber() == getRestChannel().getLogicalSlotNumber()) + if(timeslotFrequency.getNumber() == getRestChannel().getValue()) { getRestChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusSiteStatus.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusSiteStatus.java index 03f968e23..d42a49543 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusSiteStatus.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/CapacityPlusSiteStatus.java @@ -22,7 +22,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DmrRestLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.CACH; @@ -39,13 +39,11 @@ public class CapacityPlusSiteStatus extends CSBKMessage implements ITimeslotFreq private static final int[] BYTE = new int[]{0, 1, 2, 3, 4, 5, 6, 7}; private static final int[] TWO_BYTES = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; private static final int[] SEGMENT_INDICATOR = new int[]{16, 17}; - private static final int TIMESLOT = 18; - private static final int RESERVED = 19; - private static final int[] REST_LSN = new int[]{20, 21, 22, 23}; + private static final int[] REST_LSN = new int[]{19, 20, 21, 22, 23}; private static final int[] LSN_VOICE_BITMAP = new int[]{24, 25, 26, 27, 28, 29, 30, 31}; private static final int LSN_1_8_BITMAP_START = 24; - private DMRLogicalChannel mRestChannel; + private DmrRestLsn mRestChannel; private List mIdentifiers; /** @@ -80,7 +78,7 @@ public String toString() sb.append(" RAS:").append(getBPTCReservedBits()); } - sb.append(" CSBK CAP+ SITE STATUS REST ").append(getRestChannel()); + sb.append(" CSBK CAP+ SITE STATUS ").append(getRestChannel()); sb.append(" ").append(getSegmentIndicator()); sb.append(" "); @@ -277,11 +275,11 @@ public SegmentIndicator getSegmentIndicator() /** * Current rest channel for this site. */ - public DMRLogicalChannel getRestChannel() + public DmrRestLsn getRestChannel() { if(mRestChannel == null) { - mRestChannel = new DMRLogicalChannel(getRestRepeater(), getRestTimeslot()); + mRestChannel = new DmrRestLsn(getRestLSN()); } return mRestChannel; @@ -297,31 +295,13 @@ public int getRestLSN() return getMessage().getInt(REST_LSN); } - /** - * Rest Channel Repeater - */ - public int getRestRepeater() - { - return (int) Math.ceil(getRestLSN() / 2.0); - } - - /** - * Rest Channel Timeslot - * - * @return 1 or 2 - */ - public int getRestTimeslot() - { - return (getRestLSN() % 2 == 0) ? 2 : 1; - } - /** * Logical slot numbers that require slot to frequency mappings. */ @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getRestChannel().getLSNArray(); + return getRestChannel().getLogicalSlotNumbers(); } /** @@ -334,7 +314,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency : timeslotFrequencies) { - if(getRestChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber()) + if(getRestChannel().getValue() == timeslotFrequency.getNumber()) { getRestChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusDataChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusDataChannelGrant.java index 019840393..b2e56521d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusDataChannelGrant.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusDataChannelGrant.java @@ -23,7 +23,7 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; @@ -40,14 +40,13 @@ public class ConnectPlusDataChannelGrant extends CSBKMessage implements ITimeslo { private static final int[] TARGET_ADDRESS = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; - private static final int[] REPEATER = new int[]{40, 41, 42, 43}; - private static final int[] CHANNEL_GRANT_TIMESLOT = new int[]{44}; + private static final int[] REPEATER = new int[]{40, 41, 42, 43, 44}; //Analysis: this field correlates to UNKNOWN_FIELD_1(bits: 40-48) in ConnectPlusTerminateChannelGrant. private static final int[] UNKNOWN_FIELD = new int[]{48, 49, 50, 51, 52, 53, 54, 55}; private RadioIdentifier mTargetRadio; - private DMRLogicalChannel mDMRLogicalChannel; + private DMRLsn mDmrLsn; private List mIdentifiers; /** @@ -117,32 +116,23 @@ public int getRepeater() return getMessage().getInt(REPEATER); } - /** - * Channel grant timeslot - * @return 1 or 2 - */ - public int getChannelGrantTimeslot() - { - return getMessage().getInt(CHANNEL_GRANT_TIMESLOT) + 1; - } - /** * DMR Channel */ - public DMRLogicalChannel getChannel() + public DMRLsn getChannel() { - if(mDMRLogicalChannel == null) + if(mDmrLsn == null) { - mDMRLogicalChannel = new DMRLogicalChannel(getRepeater(), getChannelGrantTimeslot()); + mDmrLsn = new DMRLsn(getRepeater()); } - return mDMRLogicalChannel; + return mDmrLsn; } @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getChannel().getLSNArray(); + return getChannel().getLogicalSlotNumbers(); } /** @@ -154,7 +144,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) { - if(timeslotFrequency.getNumber() == getChannel().getLogicalSlotNumber()) + if(timeslotFrequency.getNumber() == getChannel().getValue()) { getChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusOTAAnnouncement.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusOTAAnnouncement.java index 32a9136af..a886be8e0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusOTAAnnouncement.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusOTAAnnouncement.java @@ -22,7 +22,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.CACH; @@ -44,11 +44,10 @@ public class ConnectPlusOTAAnnouncement extends CSBKMessage implements ITimeslot private static final int[] MESSAGE_TYPE = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; private static final int[] VERSION = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; private static final int[] UNKNOWN = new int[]{40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63}; - private static final int[] DATA_REPEATER = new int[]{64, 65, 66, 67}; - private static final int[] DATA_TIMESLOT = new int[]{68}; + 57, 58, 59, 60, 61, 62}; + private static final int[] DATA_REPEATER = new int[]{63, 64, 65, 66, 67}; - private DMRLogicalChannel mDataChannel; + private DMRLsn mDataChannel; private List mIdentifiers; /** @@ -107,32 +106,23 @@ public int getDataRepeater() return getMessage().getInt(DATA_REPEATER); } - /** - * Data Timeslot - * @return 1 or 2 - */ - public int getDataTimeslot() - { - return getMessage().getInt(DATA_TIMESLOT) + 1; - } - /** * DMR Channel where the data is available */ - public DMRLogicalChannel getDataChannel() + public DMRLsn getDataChannel() { if(mDataChannel == null) { - mDataChannel = new DMRLogicalChannel(getDataRepeater(), getDataTimeslot()); + mDataChannel = new DMRLsn(getDataRepeater()); } return mDataChannel; } @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getDataChannel().getLSNArray(); + return getDataChannel().getLogicalSlotNumbers(); } /** @@ -145,7 +135,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency : timeslotFrequencies) { - if(timeslotFrequency.getNumber() == getDataChannel().getLogicalSlotNumber()) + if(timeslotFrequency.getNumber() == getDataChannel().getValue()) { getDataChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusVoiceChannelUser.java index e16986c38..ea6741639 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusVoiceChannelUser.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/motorola/ConnectPlusVoiceChannelUser.java @@ -24,7 +24,7 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; @@ -44,13 +44,12 @@ public class ConnectPlusVoiceChannelUser extends CSBKMessage implements ITimeslo 32, 33, 34, 35, 36, 37, 38, 39}; private static final int[] GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; - private static final int[] TRAFFIC_CHANNEL_REPEATER = new int[]{64, 65, 66, 67}; - private static final int[] TRAFFIC_CHANNEL_TIMESLOT = new int[]{68}; + private static final int[] TRAFFIC_CHANNEL_REPEATER = new int[]{64, 65, 66, 67, 68}; private static final int[] UNKNOWN_FIELD = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; private RadioIdentifier mRadio; private TalkgroupIdentifier mTalkgroup; - private DMRLogicalChannel mDMRLogicalChannel; + private DMRLsn mDmrLsn; private List mIdentifiers; /** @@ -132,32 +131,23 @@ public int getTrafficChannelRepeater() return getMessage().getInt(TRAFFIC_CHANNEL_REPEATER); } - /** - * Traffic channel timeslot - * @return 1 or 2 - */ - public int getTrafficChannelTimeslot() - { - return getMessage().getInt(TRAFFIC_CHANNEL_TIMESLOT) + 1; - } - /** * DMR Channel */ - public DMRLogicalChannel getChannel() + public DMRLsn getChannel() { - if(mDMRLogicalChannel == null) + if(mDmrLsn == null) { - mDMRLogicalChannel = new DMRLogicalChannel(getTrafficChannelRepeater(), getTrafficChannelTimeslot()); + mDmrLsn = new DMRLsn(getTrafficChannelRepeater()); } - return mDMRLogicalChannel; + return mDmrLsn; } @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getChannel().getLSNArray(); + return getChannel().getLogicalSlotNumbers(); } /** @@ -169,7 +159,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) { - if(timeslotFrequency.getNumber() == getChannel().getLogicalSlotNumber()) + if(timeslotFrequency.getNumber() == getChannel().getValue()) { getChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java index da7e2466e..d99632c14 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java @@ -25,7 +25,7 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRLsn; import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; @@ -224,11 +224,11 @@ public DMRChannel getMoveToChannel() * Logical Slot Number(s) for channels contained in this message */ @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - if(getMoveToChannel() instanceof DMRLogicalChannel) + if(getMoveToChannel() instanceof DMRLsn dmrLsn) { - return ((DMRLogicalChannel)getMoveToChannel()).getLSNArray(); + return dmrLsn.getLogicalSlotNumbers(); } return new int[0]; @@ -241,13 +241,13 @@ public int[] getLogicalTimeslotNumbers() @Override public void apply(List timeslotFrequencies) { - if(getMoveToChannel() instanceof DMRLogicalChannel) + if(getMoveToChannel() instanceof DMRTier3Channel) { - DMRLogicalChannel channel = (DMRLogicalChannel)getMoveToChannel(); + DMRTier3Channel channel = (DMRTier3Channel)getMoveToChannel(); for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) { - if(channel.getLogicalSlotNumber() == timeslotFrequency.getNumber()) + if(channel.getValue() == timeslotFrequency.getNumber()) { channel.setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java index 3905a3f25..73121bdec 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -23,24 +23,20 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; -import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; -import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.CACH; import io.github.dsheirer.module.decode.dmr.message.data.SlotType; import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock; import io.github.dsheirer.module.decode.dmr.message.type.AbsoluteChannelParameters; import io.github.dsheirer.module.decode.dmr.message.type.DataType; import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode; - import java.util.ArrayList; import java.util.List; /** * DMR Tier III - Announcement Message - Adjacent/Neighbor Site Information */ -public class AdjacentSiteInformation extends Announcement implements ITimeslotFrequencyReceiver +public class AdjacentSiteInformation extends Announcement { private static final int NEIGHBOR_SYSTEM_IDENTITY_CODE_OFFSET = 21; private static final int NETWORK_CONNECTION_STATUS_AVAILABLE_FLAG = 56; @@ -235,39 +231,4 @@ public List getIdentifiers() return mIdentifiers; } - - /** - * Logical Slot Number(s) for channels contained in this message - */ - @Override - public int[] getLogicalTimeslotNumbers() - { - if(getNeighborChannel() instanceof DMRLogicalChannel) - { - return ((DMRLogicalChannel)getNeighborChannel()).getLSNArray(); - } - - return new int[0]; - } - - /** - * Applies the timeslot frequency lookup information to channels contained in this message - * @param timeslotFrequencies that match the logical timeslots - */ - @Override - public void apply(List timeslotFrequencies) - { - if(getNeighborChannel() instanceof DMRLogicalChannel) - { - DMRLogicalChannel channel = (DMRLogicalChannel)getNeighborChannel(); - - for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) - { - if(channel.getLogicalSlotNumber() == timeslotFrequency.getNumber()) - { - channel.setTimeslotFrequency(timeslotFrequency); - } - } - } - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java index 4dd2ca297..b1674fde3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock; import io.github.dsheirer.module.decode.dmr.message.type.AbsoluteChannelParameters; import io.github.dsheirer.module.decode.dmr.message.type.DataType; - import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java index 96ad4d92a..c0c5c6dab 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -23,7 +23,7 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRLsn; import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; @@ -33,7 +33,6 @@ import io.github.dsheirer.module.decode.dmr.message.type.AbsoluteChannelParameters; import io.github.dsheirer.module.decode.dmr.message.type.DataType; import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode; - import java.util.ArrayList; import java.util.List; @@ -240,11 +239,11 @@ public List getIdentifiers() * Logical Slot Number(s) for channels contained in this message */ @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - if(getChannel() instanceof DMRLogicalChannel) + if(getChannel() instanceof DMRLsn dmrLsn) { - return ((DMRLogicalChannel)getChannel()).getLSNArray(); + return dmrLsn.getLogicalSlotNumbers(); } return new int[0]; @@ -257,13 +256,13 @@ public int[] getLogicalTimeslotNumbers() @Override public void apply(List timeslotFrequencies) { - if(getChannel() instanceof DMRLogicalChannel) + if(getChannel() instanceof DMRTier3Channel) { - DMRLogicalChannel channel = (DMRLogicalChannel)getChannel(); + DMRTier3Channel channel = (DMRTier3Channel)getChannel(); for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) { - if(channel.getLogicalSlotNumber() == timeslotFrequency.getNumber()) + if(channel.getValue() == timeslotFrequency.getNumber()) { channel.setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java index 47e54a3a2..6229430e4 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -22,7 +22,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRLsn; import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; @@ -32,7 +32,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock; import io.github.dsheirer.module.decode.dmr.message.type.AbsoluteChannelParameters; import io.github.dsheirer.module.decode.dmr.message.type.DataType; - import java.util.List; /** @@ -159,11 +158,11 @@ public DMRChannel getChannel() * Logical Slot Number(s) for channels contained in this message */ @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - if(getChannel() instanceof DMRLogicalChannel) + if(getChannel() instanceof DMRLsn dmrLsn) { - return ((DMRLogicalChannel)getChannel()).getLSNArray(); + return dmrLsn.getLogicalSlotNumbers(); } return new int[0]; @@ -176,13 +175,13 @@ public int[] getLogicalTimeslotNumbers() @Override public void apply(List timeslotFrequencies) { - if(getChannel() instanceof DMRLogicalChannel) + if(getChannel() instanceof DMRTier3Channel) { - DMRLogicalChannel channel = (DMRLogicalChannel)getChannel(); + DMRTier3Channel channel = (DMRTier3Channel)getChannel(); for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) { - if(channel.getLogicalSlotNumber() == timeslotFrequency.getNumber()) + if(channel.getValue() == timeslotFrequency.getNumber()) { channel.setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/DefinedShortDataHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/DefinedShortDataHeader.java index 89828448b..893b5fd5c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/DefinedShortDataHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/DefinedShortDataHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -25,7 +25,6 @@ import io.github.dsheirer.module.decode.dmr.message.CACH; import io.github.dsheirer.module.decode.dmr.message.data.SlotType; import io.github.dsheirer.module.decode.dmr.message.type.DefinedDataFormat; - import java.util.ArrayList; import java.util.List; @@ -39,6 +38,7 @@ public class DefinedShortDataHeader extends PacketSequenceHeader private static final int RESYNCHRONIZE_FLAG = 70; private static final int FULL_MESSAGE_FLAG = 71; private static final int[] BIT_PADDING = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] HEADER_CRC = new int[]{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; private List mIdentifiers; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/ProprietaryDataHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/ProprietaryDataHeader.java index 2f40f045f..33b8eef1d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/ProprietaryDataHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/ProprietaryDataHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -34,6 +34,11 @@ public class ProprietaryDataHeader extends DataHeader { private static final int[] SERVICE_ACCESS_POINT = new int[]{0, 1, 2, 3}; private static final int[] VENDOR = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] VENDOR_DATA = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; + private static final int[] HEADER_CRC = new int[]{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; /** * Constructs an instance. @@ -62,10 +67,20 @@ public String toString() sb.append(" PROPRIETARY DATA HEADER"); sb.append(" VENDOR:").append(getVendor()); sb.append(" ").append(getServiceAccessPoint()); + sb.append(" VENDOR DATA:").append(getVendorData()); sb.append(" MSG:").append(getMessage().toHexString()); return sb.toString(); } + /** + * Vendor defined data payload from this proprietary header. + * @return hex values. + */ + public String getVendorData() + { + return getMessage().getHex(VENDOR_DATA, 16); + } + /** * Optional packet prefix. * @return packet prefix fragment or null. diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java index 523b56835..b3346d518 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MNISProprietaryDataHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -66,7 +66,8 @@ public MNISProprietaryDataHeader(DMRSyncPattern syncPattern, CorrectedBinaryMess public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("MOTOROLA MNIS HEADER"); + sb.append("CC:").append(getSlotType().getColorCode()); + sb.append(" MOTOROLA MNIS HEADER"); if(getApplicationType() == ApplicationType.UNKNOWN) { sb.append(" APPLICATION TYPE:0x").append(Integer.toHexString(getApplicationTypeNumber()).toUpperCase()); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MotorolaProprietaryDataHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MotorolaDataEncryptionHeader.java similarity index 62% rename from src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MotorolaProprietaryDataHeader.java rename to src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MotorolaDataEncryptionHeader.java index 4e0070df9..0bad828a2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MotorolaProprietaryDataHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/header/motorola/MotorolaDataEncryptionHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -31,10 +31,15 @@ /** * Motorola Proprietary Data Header */ -public class MotorolaProprietaryDataHeader extends ProprietaryDataHeader +public class MotorolaDataEncryptionHeader extends ProprietaryDataHeader { private static final int[] SERVICE_ACCESS_POINT = new int[]{0, 1, 2, 3}; private static final int[] VENDOR = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; + private static final int[] ALGORITHM = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] KEY_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31}; + private static final int[] UNKNOWN = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] INITIALIZATION_VECTOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}; /** * Constructs an instance. @@ -46,7 +51,7 @@ public class MotorolaProprietaryDataHeader extends ProprietaryDataHeader * @param timestamp message was received * @param timeslot for the DMR burst */ - public MotorolaProprietaryDataHeader(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot) + public MotorolaDataEncryptionHeader(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot) { super(syncPattern, message, cach, slotType, timestamp, timeslot); } @@ -55,14 +60,57 @@ public MotorolaProprietaryDataHeader(DMRSyncPattern syncPattern, CorrectedBinary public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("MOTOROLA PROPRIETARY DATA HEADER"); - sb.append(" ").append(getServiceAccessPoint()); + sb.append("CC:").append(getSlotType().getColorCode()); + sb.append(" MOTOROLA DATA ENCRYPTION HEADER"); + sb.append(" SAP:").append(getServiceAccessPoint()); + sb.append(" ALGORITHM?:").append(getAlgorithm()); + sb.append(" KEY?:").append(getKeyId()); + sb.append(" IV?:").append(getInitializationVector()); + sb.append(" UNK:").append(getUnknown()); sb.append(" MSG:").append(getMessage().toHexString()); return sb.toString(); } + /** + * Unknown message field(s). + * @return hex value. + */ + public String getUnknown() + { + return getMessage().getHex(UNKNOWN, 4); + } + + /** + * Encryption key ID + * @return key ID + */ + public int getKeyId() + { + return getMessage().getInt(KEY_ID); + } + + /** + * Encryption Algorithm + * @return algorithm ID + */ + public int getAlgorithm() + { + return getMessage().getInt(ALGORITHM); + } + + /** + * Encryption initialization vector + * + * @return vector in hex + */ + public String getInitializationVector() + { + return getMessage().getHex(INITIALIZATION_VECTOR, 8); + } + /** * Utility method to lookup the vendor from a CSBK message + * * @param message containing CSBK bits * @return vendor */ diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java index 62736700d..ba5d20da8 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java @@ -37,8 +37,9 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraTerminator; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraUnitToUnitVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusEncryptedVoiceChannelUser; -import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusWideAreaVoiceChannelUser; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaEncryptionParameters; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaGroupVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ActivityUpdateMessage; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.CapacityPlusRestChannel; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ConnectPlusControlChannel; @@ -113,7 +114,7 @@ else if(message.size() == 96) flc = new TerminatorData(message, timestamp, timeslot); break; case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER: - flc = new CapacityPlusGroupVoiceChannelUser(message, timestamp, timeslot); + flc = new MotorolaGroupVoiceChannelUser(message, timestamp, timeslot); break; case FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER: flc = new CapacityPlusEncryptedVoiceChannelUser(message, timestamp, timeslot); @@ -121,7 +122,9 @@ else if(message.size() == 96) case FULL_CAPACITY_PLUS_WIDE_AREA_VOICE_CHANNEL_USER: flc = new CapacityPlusWideAreaVoiceChannelUser(message, timestamp, timeslot); break; - + case FULL_CAPACITY_PLUS_ENCRYPTION_PARAMETERS: + flc = new MotorolaEncryptionParameters(message, timestamp, timeslot); + break; case FULL_HYTERA_GROUP_VOICE_CHANNEL_USER: flc = new HyteraGroupVoiceChannelUser(message, timestamp, timeslot); break; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java index a35dab54c..5fc477a3e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java @@ -48,6 +48,8 @@ public enum LCOpcode FULL_CAPACITY_PLUS_WIDE_AREA_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 4, "WAN GROUP VOICE CHANNEL USER"), //Observed on Cap+ Multi-Site System during an encrypted voice call FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 32, "ENCRYPTED VOICE CHANNEL USER"), + //Observed on Cap+ Multi-Site System during an encrypted voice call + FULL_CAPACITY_PLUS_ENCRYPTION_PARAMETERS(Vendor.MOTOROLA_CAPACITY_PLUS, true, 33, "ENCRYPTION PARAMETERS"), //Cap+ opcodes from https://forums.radioreference.com/threads/understanding-capacity-plus-trunking-some-more.452566/ //FLCO 0: Group Call Maintenance //FLCO 3: Private Call Maintenance (TermLC) diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/AbstractVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/AbstractVoiceChannelUser.java index 635bd5310..b4d78c962 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/AbstractVoiceChannelUser.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/AbstractVoiceChannelUser.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -20,6 +20,7 @@ package io.github.dsheirer.module.decode.dmr.message.data.lc.full; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.dmr.message.IServiceOptionsProvider; import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions; /** @@ -27,7 +28,7 @@ * * ETSI TS 102 361-2 7.1.1.2 */ -public abstract class AbstractVoiceChannelUser extends FullLCMessage +public abstract class AbstractVoiceChannelUser extends FullLCMessage implements IServiceOptionsProvider { private static final int[] SERVICE_OPTIONS = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraFullLC.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraFullLC.java index 8d40334d3..2c7936543 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraFullLC.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraFullLC.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -22,13 +22,14 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; +import io.github.dsheirer.module.decode.dmr.message.IServiceOptionsProvider; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions; /** * Hytera Full Link Control */ -public abstract class HyteraFullLC extends FullLCMessage +public abstract class HyteraFullLC extends FullLCMessage implements IServiceOptionsProvider { private static final int[] SERVICE_OPTIONS = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; private static final int[] FREE_REPEATER = new int[]{24, 25, 26, 27}; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusEncryptedVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusEncryptedVoiceChannelUser.java index b3917c03f..f822d573f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusEncryptedVoiceChannelUser.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusEncryptedVoiceChannelUser.java @@ -37,7 +37,7 @@ public class CapacityPlusEncryptedVoiceChannelUser extends CapacityPlusVoiceChan private static final int[] TARGET_ADDRESS = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; private static final int[] UNKNOWN_2 = new int[]{48, 49, 50, 51, 52, 53, 54, 55}; private static final int[] SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; - private static final int[] UNKNOWN_3 = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; + //Reed Solomon FEC: 72-95 private RadioIdentifier mRadio; private TalkgroupIdentifier mTalkgroup; @@ -94,10 +94,6 @@ public String getUnknown2() { return getMessage().getHex(UNKNOWN_2, 2); } - public String getUnknown3() - { - return getMessage().getHex(UNKNOWN_3, 2); - } /** * Source radio address diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusGroupVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusGroupVoiceChannelUser.java deleted file mode 100644 index c496aa452..000000000 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusGroupVoiceChannelUser.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2023 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.data.lc.full.motorola; - -import io.github.dsheirer.bits.CorrectedBinaryMessage; -import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.radio.RadioIdentifier; -import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; -import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; -import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; -import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; -import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup; -import java.util.ArrayList; -import java.util.List; - -/** - * Motorola Capacity Plus - Group Voice Channel User - */ -public class CapacityPlusGroupVoiceChannelUser extends CapacityPlusVoiceChannelUser implements ITimeslotFrequencyReceiver -{ - private static final int[] CAPACITY_PLUS_GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47}; - private static final int[] CONVENTIONAL_GROUP_ADDRESS = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; - private static final int[] REST_CHANNEL = new int[]{51, 52, 53, 54}; - private static final int[] REST_CHANNEL_TIMESLOT = new int[]{55}; - private static final int[] CAPACITY_PLUS_SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; - private static final int[] CONVENTIONAL_SOURCE_ADDRESS = new int[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; - private static final int[] UNKNOWN = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; - - private RadioIdentifier mRadio; - private TalkgroupIdentifier mTalkgroup; - private DMRLogicalChannel mRestChannel; - private List mIdentifiers; - - /** - * Constructs an instance. - * - * @param message for the link control payload - */ - public CapacityPlusGroupVoiceChannelUser(CorrectedBinaryMessage message, long timestamp, int timeslot) - { - super(message, timestamp, timeslot); - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - - if(!isValid()) - { - sb.append("[CRC-ERROR] "); - } - - if(isEncrypted()) - { - sb.append(" ENCRYPTED"); - } - - if(isReservedBitSet()) - { - sb.append(" RESERVED-BIT"); - } - - if(getServiceOptions().isCapacityPlus()) - { - sb.append("FLC MOTOROLA CAP+ GROUP VOICE CHANNEL USER"); - } - else - { - sb.append("FLC MOTOROLA CONV/IP SITE GROUP VOICE CHANNEL USER"); - } - sb.append(" FM:").append(getRadio()); - sb.append(" TO:").append(getTalkgroup()); - - if(hasRestChannel()) - { - sb.append(" REST:"); - sb.append(getRestChannel()); - } - - sb.append(" ").append(getServiceOptions()); - sb.append(" MSG:").append(getMessage().toHexString()); - return sb.toString(); - } - - /** - * Unknown 8-bit field - */ - public String getUnknown() - { - return getMessage().getHex(UNKNOWN, 2); - } - - /** - * Logical channel number (ie repeater number). - */ - public DMRLogicalChannel getRestChannel() - { - if(mRestChannel == null) - { - mRestChannel = new DMRLogicalChannel(getRestChannelRepeater(), getRestChannelTimeslot()); - } - - return mRestChannel; - } - - /** - * Rest repeater number - */ - public int getRestChannelRepeater() - { - return getMessage().getInt(REST_CHANNEL) + 1; - } - - /** - * Rest timeslot - * - * @return - */ - public int getRestChannelTimeslot() - { - return getMessage().getInt(REST_CHANNEL_TIMESLOT) + 1; - } - - /** - * Indicates if this message has a rest channel indicated for the call - */ - public boolean hasRestChannel() - { - return getServiceOptions().isCapacityPlus() && getRestChannelRepeater() != 0; - } - - /** - * Source radio address - */ - public RadioIdentifier getRadio() - { - if(mRadio == null) - { - if(getServiceOptions().isCapacityPlus()) - { - mRadio = DMRRadio.createFrom(getMessage().getInt(CAPACITY_PLUS_SOURCE_ADDRESS)); - } - else - { - mRadio = DMRRadio.createFrom(getMessage().getInt(CONVENTIONAL_SOURCE_ADDRESS)); - } - } - - return mRadio; - } - - /** - * Talkgroup address - */ - public TalkgroupIdentifier getTalkgroup() - { - if(mTalkgroup == null) - { - if(getServiceOptions().isCapacityPlus()) - { - mTalkgroup = DMRTalkgroup.create(getMessage().getInt(CAPACITY_PLUS_GROUP_ADDRESS)); - } - else - { - mTalkgroup = DMRTalkgroup.create(getMessage().getInt(CONVENTIONAL_GROUP_ADDRESS)); - } - } - - return mTalkgroup; - } - - @Override - public List getIdentifiers() - { - if(mIdentifiers == null) - { - mIdentifiers = new ArrayList<>(); - mIdentifiers.add(getTalkgroup()); - mIdentifiers.add(getRadio()); - - if(hasRestChannel()) - { - mIdentifiers.add(getRestChannel()); - } - } - - return mIdentifiers; - } - - /** - * Exposes the rest channel logical slot number so that a LSN to frequency map can be applied to this message. - */ - @Override - public int[] getLogicalTimeslotNumbers() - { - return getRestChannel().getLSNArray(); - } - - /** - * Applies the LSN to frequency map to the rest channel. - * - * @param timeslotFrequencies that match the logical timeslots - */ - @Override - public void apply(List timeslotFrequencies) - { - for(TimeslotFrequency timeslotFrequency : timeslotFrequencies) - { - if(getRestChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber()) - { - getRestChannel().setTimeslotFrequency(timeslotFrequency); - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java index 9c80a9e89..2a8da6c51 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java @@ -20,15 +20,17 @@ package io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.dmr.message.IServiceOptionsProvider; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; import io.github.dsheirer.module.decode.dmr.message.type.CapacityPlusServiceOptions; /** * Any Capacity Plus Voice Channel User link control message that contains vendor-specific service options. */ -public abstract class CapacityPlusVoiceChannelUser extends FullLCMessage +public abstract class CapacityPlusVoiceChannelUser extends FullLCMessage implements IServiceOptionsProvider { private static final int[] SERVICE_OPTIONS = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; + //Reed Solomon FEC: 72-95 private CapacityPlusServiceOptions mServiceOptions; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusWideAreaVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusWideAreaVoiceChannelUser.java index 361093b6b..f44af10e4 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusWideAreaVoiceChannelUser.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusWideAreaVoiceChannelUser.java @@ -23,7 +23,7 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DmrRestLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; @@ -32,21 +32,23 @@ import java.util.List; /** - * Motorola Capacity Plus - Wide Area (Multi-Site) Voice Channel User + * Motorola Capacity Plus - Wide Area (Linked or Multi-Site) Voice Channel User + * + * Note: in Linked Capacity Plus talkgroups range 1-255 and radio IDs range 1-65535 */ public class CapacityPlusWideAreaVoiceChannelUser extends CapacityPlusVoiceChannelUser implements ITimeslotFrequencyReceiver { - private static final int[] UNKNOWN_1 = new int[]{24, 25, 26, 27, 28, 29, 30, 31}; - private static final int[] GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47}; - //private static final int[] REST_REPEATER = new int[]{51, 52, 53, 54}; - //private static final int[] REST_TIMESLOT = new int[]{55}; - private static final int[] REST_LSN = new int[]{52, 53, 54, 55}; - private static final int[] SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; - private static final int[] UNKNOWN_3 = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; + //Bits 16-23: Service Options + private static final int[] UNKNOWN_1 = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; + private static final int[] GROUP_ID = new int[]{40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] UNUSED = new int[]{48, 49, 50}; + private static final int[] REST_LSN = new int[]{51, 52, 53, 54, 55}; + private static final int[] RADIO_ID = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + //Reed Solomon FEC: 72-95 private RadioIdentifier mRadio; private TalkgroupIdentifier mTalkgroup; - private DMRLogicalChannel mRestChannel; + private DmrRestLsn mRestChannel; private List mIdentifiers; /** @@ -82,18 +84,12 @@ public String toString() sb.append("FLC MOTOROLA CAP+ WIDE-AREA VOICE CHANNEL USER FM:"); sb.append(getRadio()); sb.append(" TO:").append(getTalkgroup()); - sb.append(" REST:"); if(hasRestChannel()) { - sb.append(getRestChannel()); - } - else - { - sb.append("--"); + sb.append(" ").append(getRestChannel()); } - sb.append(" UNK1:").append(getUnknown1()); - sb.append(" UNK2:").append(getUnknown2()); sb.append(" ").append(getServiceOptions()); + sb.append(" UNK1:").append(getUnknown1()); sb.append(" MSG:").append(getMessage().toHexString()); return sb.toString(); } @@ -101,27 +97,19 @@ public String toString() /** * Unknown1 8-bit field */ - public int getUnknown1() - { - return getMessage().getInt(UNKNOWN_1); - } - - /** - * Unknown2 8-bit field - */ - public int getUnknown2() + public String getUnknown1() { - return getMessage().getInt(UNKNOWN_3); + return getMessage().getHex(UNKNOWN_1, 4); } /** * Logical channel number (ie repeater number). */ - public DMRLogicalChannel getRestChannel() + public DmrRestLsn getRestChannel() { if(mRestChannel == null) { - mRestChannel = new DMRLogicalChannel(getRestRepeater(), getRestTimeslot()); + mRestChannel = new DmrRestLsn(getRestLSN()); } return mRestChannel; @@ -136,25 +124,12 @@ public int getRestLSN() return getMessage().getInt(REST_LSN); } - /** - * Rest channel timeslot - */ - public int getRestTimeslot() - { - return (getRestLSN() % 2 == 0) ? 2 : 1; - } - - /** - * Rest channel repeater number - */ - public int getRestRepeater() { return (int) Math.ceil(getRestLSN() / 2.0); } - /** * Indicates if this message has a reset channel defined. */ public boolean hasRestChannel() { - return getRestRepeater() != 0; + return getRestLSN() != 0; } /** @@ -164,7 +139,7 @@ public RadioIdentifier getRadio() { if(mRadio == null) { - mRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_ADDRESS)); + mRadio = DMRRadio.createFrom(getMessage().getInt(RADIO_ID)); } return mRadio; @@ -177,7 +152,7 @@ public TalkgroupIdentifier getTalkgroup() { if(mTalkgroup == null) { - mTalkgroup = DMRTalkgroup.create(getMessage().getInt(GROUP_ADDRESS)); + mTalkgroup = DMRTalkgroup.create(getMessage().getInt(GROUP_ID)); } return mTalkgroup; @@ -201,9 +176,9 @@ public List getIdentifiers() * Exposes the rest channel logical slot number so that a LSN to frequency map can be applied to this message. */ @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getRestChannel().getLSNArray(); + return getRestChannel().getLogicalSlotNumbers(); } /** @@ -216,7 +191,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency : timeslotFrequencies) { - if(getRestChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber()) + if(getRestChannel().getValue() == timeslotFrequency.getNumber()) { getRestChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaEncryptionParameters.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaEncryptionParameters.java new file mode 100644 index 000000000..ae5d53188 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaEncryptionParameters.java @@ -0,0 +1,128 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.data.lc.full.motorola; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; +import java.util.ArrayList; +import java.util.List; + +/** + * Motorola Encryption Parameters + *

+ * Note: observed as FLC payload for a PI_HEADER slot type. + * Note: observed on a possible Hytera (clone) system that was configured as IP Site Connect compatible. + */ +public class MotorolaEncryptionParameters extends FullLCMessage +{ + private static final int[] KEY_ID = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] INITIALIZATION_VECTOR = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] ALGORITHM = new int[]{56, 57, 58, 59, 60, 61, 62, 63}; + private static final int[] DESTINATION_GROUP = new int[]{64, 65, 66, 67, 68, 69, 70, 71}; + //Reed Solomon FEC: 72-95 + + private DMRTalkgroup mTalkgroup; + private List mIdentifiers; + + /** + * Constructs an instance. + * + * @param message for the link control payload + */ + public MotorolaEncryptionParameters(CorrectedBinaryMessage message, long timestamp, int timeslot) + { + super(message, timestamp, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("[CRC-ERROR] "); + } + + if(isEncrypted()) + { + sb.append(" *ENCRYPTED*"); + } + + if(isReservedBitSet()) + { + sb.append(" *RESERVED-BIT*"); + } + + sb.append("FLC MOTOROLA ENCRYPTION PARAMETERS - ALGORITHM:").append(getAlgorithm()); + sb.append(" KEY:").append(getKeyId()); + sb.append(" IV?:").append(getInitializationVector()); + sb.append(" TALKGROUP:").append(getTalkgroup()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + public DMRTalkgroup getTalkgroup() + { + if(mTalkgroup == null) + { + mTalkgroup = new DMRTalkgroup(getMessage().getInt(DESTINATION_GROUP)); + } + + return mTalkgroup; + } + + public int getKeyId() + { + return getMessage().getInt(KEY_ID); + } + + public String getAlgorithm() + { + int algorithm = getMessage().getInt(ALGORITHM); + + if(algorithm == 0) + { + return "EP/ARC4"; + } + + return "UNK(" + algorithm + ")"; + } + + public String getInitializationVector() + { + return getMessage().getHex(INITIALIZATION_VECTOR, 8); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaGroupVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaGroupVoiceChannelUser.java new file mode 100644 index 000000000..4c5ecf491 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/MotorolaGroupVoiceChannelUser.java @@ -0,0 +1,122 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.data.lc.full.motorola; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.identifier.radio.RadioIdentifier; +import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; +import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; +import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup; +import java.util.ArrayList; +import java.util.List; + +/** + * Motorola Capacity Plus - Group Voice Channel User + */ +public class MotorolaGroupVoiceChannelUser extends CapacityPlusVoiceChannelUser +{ + private static final int[] GROUP_ADDRESS = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int[] SOURCE_ADDRESS = new int[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + //Reed Solomon FEC: 72-95 + + private RadioIdentifier mRadio; + private TalkgroupIdentifier mTalkgroup; + private List mIdentifiers; + + /** + * Constructs an instance. + * + * @param message for the link control payload + */ + public MotorolaGroupVoiceChannelUser(CorrectedBinaryMessage message, long timestamp, int timeslot) + { + super(message, timestamp, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("[CRC-ERROR] "); + } + + if(isEncrypted()) + { + sb.append(" ENCRYPTED"); + } + + if(isReservedBitSet()) + { + sb.append(" RESERVED-BIT"); + } + + sb.append("FLC MOTOROLA GROUP VOICE CHANNEL USER"); + sb.append(" FM:").append(getRadio()); + sb.append(" TO:").append(getTalkgroup()); + sb.append(" ").append(getServiceOptions()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + /** + * Source radio address + */ + public RadioIdentifier getRadio() + { + if(mRadio == null) + { + mRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_ADDRESS)); + } + + return mRadio; + } + + /** + * Talkgroup address + */ + public TalkgroupIdentifier getTalkgroup() + { + if(mTalkgroup == null) + { + mTalkgroup = DMRTalkgroup.create(getMessage().getInt(GROUP_ADDRESS)); + } + + return mTalkgroup; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + mIdentifiers.add(getRadio()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/ActivityUpdateMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/ActivityUpdateMessage.java index 1c1763c42..8eb7fa64d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/ActivityUpdateMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/ActivityUpdateMessage.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -22,7 +22,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.message.type.Activity; - import java.util.Collections; import java.util.List; @@ -55,13 +54,18 @@ public String toString() { sb.append("[CRC ERROR] "); } - - sb.append("SLC ACTIVITY UPDATE TS1 ["); - sb.append(getHashAddressTS1()).append("] "); + sb.append("SLC TS1:"); sb.append(getActivityTS1()); - sb.append(" / TS2 ["); - sb.append(getHashAddressTS2()).append("] "); + if(getActivityTS1() != Activity.IDLE) + { + sb.append(" [").append(getHashAddressTS1()).append("]"); + } + sb.append(" TS2:"); sb.append(getActivityTS2()); + if(getActivityTS2() != Activity.IDLE) + { + sb.append(" [").append(getHashAddressTS2()).append("]"); + } sb.append(" MSG:").append(getMessage().toHexString()); return sb.toString(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/CapacityPlusRestChannel.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/CapacityPlusRestChannel.java index f8896947c..b3e3e893b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/CapacityPlusRestChannel.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/CapacityPlusRestChannel.java @@ -21,7 +21,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DmrRestLsn; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.identifier.DMRSite; @@ -34,13 +34,11 @@ public class CapacityPlusRestChannel extends ShortLCMessage implements ITimeslotFrequencyReceiver { private static final int[] UNKNOWN = new int[]{12, 13, 14}; - //private static final int[] REST_REPEATER = new int[]{15, 16, 17, 18}; private static final int[] REST_LSN = new int[]{15, 16, 17, 18, 19}; - //private static final int[] REST_TIMESLOT = new int[]{19}; private static final int[] SITE = new int[]{20, 21, 22, 23, 24}; private static final int[] UNKNOWN_2 = new int[]{25, 26, 27}; - private DMRLogicalChannel mRestChannel; + private DmrRestLsn mRestChannel; private DMRSite mSite; private List mIdentifiers; @@ -63,7 +61,7 @@ public String toString() sb.append("[CRC ERROR] "); } sb.append("SLC MOTOROLA CAP+ SITE:").append(getSite()); - sb.append(" REST:").append(getRestChannel()); + sb.append(" ").append(getRestChannel()); sb.append(" MSG:").append(getMessage().toHexString()); return sb.toString(); } @@ -84,11 +82,11 @@ public DMRSite getSite() /** * Rest Channel Number */ - public DMRLogicalChannel getRestChannel() + public DmrRestLsn getRestChannel() { if(mRestChannel == null) { - mRestChannel = new DMRLogicalChannel(getRestRepeater(), getRestTimeslot()); + mRestChannel = new DmrRestLsn(getRestLSN()); } return mRestChannel; @@ -103,26 +101,13 @@ public int getRestLSN() return getMessage().getInt(REST_LSN); } - /** - * Rest repeater - */ - public int getRestRepeater() { return (int) Math.ceil(getRestLSN() / 2.0); } - - /** - * Rest timeslot - */ - public int getRestTimeslot() - { - return (getRestLSN() % 2 == 0) ? 2 : 1; - } - /** * Exposes the rest channel logical slot number so that a LSN to frequency map can be applied to this message. */ @Override - public int[] getLogicalTimeslotNumbers() + public int[] getLogicalSlotNumbers() { - return getRestChannel().getLSNArray(); + return getRestChannel().getLogicalSlotNumbers(); } /** @@ -134,7 +119,7 @@ public void apply(List timeslotFrequencies) { for(TimeslotFrequency timeslotFrequency: timeslotFrequencies) { - if(getRestChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber()) + if(getRestChannel().getValue() == timeslotFrequency.getNumber()) { getRestChannel().setTimeslotFrequency(timeslotFrequency); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/NullMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/NullMessage.java index 8a18ce85f..ef96de9fe 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/NullMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/NullMessage.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -21,7 +21,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; - import java.util.Collections; import java.util.List; @@ -48,8 +47,7 @@ public String toString() { sb.append("[CRC ERROR] "); } - sb.append("SLC IDLE/NULL MESSAGE"); - sb.append(" MSG:").append(getMessage().toHexString()); + sb.append("SLC TS1:IDLE TS2:IDLE"); return sb.toString(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/DMRPacketMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/DMRPacketMessage.java index 063295a90..6d7dfcb62 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/DMRPacketMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/DMRPacketMessage.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -23,7 +23,6 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.message.DMRMessage; import io.github.dsheirer.module.decode.ip.IPacket; - import java.util.ArrayList; import java.util.List; @@ -75,6 +74,10 @@ public String toString() sb.append("CC:").append(getPacketSequence().getPacketSequenceHeader().getSlotType().getColorCode()); sb.append(" FM:").append(getPacketSequence().getPacketSequenceHeader().getSourceLLID()); sb.append(" TO:").append(getPacketSequence().getPacketSequenceHeader().getDestinationLLID()); + if(getPacketSequence().isEncrypted()) + { + sb.append(" ENCRYPTED"); + } sb.append(" ").append(getPacket().toString()); return sb.toString(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequence.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequence.java index 9136b1519..d22bc6812 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequence.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequence.java @@ -24,6 +24,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.header.PacketSequenceHeader; import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataHeader; import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader; +import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MotorolaDataEncryptionHeader; import java.util.ArrayList; import java.util.List; @@ -89,6 +90,15 @@ public boolean isComplete() return false; } + /** + * Indicates if this sequence contains a Motorola data encryption header. + * @return true if so + */ + public boolean isEncrypted() + { + return hasProprietaryDataHeader() && getProprietaryDataHeader() instanceof MotorolaDataEncryptionHeader; + } + public int getTimeslot() { return mTimeslot; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java index cb444b37d..4a27855c0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java @@ -29,7 +29,10 @@ import io.github.dsheirer.module.decode.dmr.message.data.header.UDTHeader; import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraProprietaryDataHeader; import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MNISProprietaryDataHeader; +import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MotorolaDataEncryptionHeader; import io.github.dsheirer.module.decode.dmr.message.type.ApplicationType; +import io.github.dsheirer.module.decode.dmr.message.type.DataPacketFormat; +import io.github.dsheirer.module.decode.ip.DefinedShortDataPacket; import io.github.dsheirer.module.decode.ip.UnknownPacket; import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraTokenHeader; import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraUnknownPacket; @@ -179,9 +182,20 @@ else if(secondaryHeader instanceof HyteraProprietaryDataHeader) packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp()); } } + else if(secondaryHeader instanceof MotorolaDataEncryptionHeader && + packetSequence.getPacketSequenceHeader().getDataPacketFormat() == DataPacketFormat.DEFINED_SHORT_DATA) + { + return createDefinedShortData(packetSequence, packet); + } else { - mLog.info("Unknown Proprietary Packet Header Type - creating unknown packet."); + if(packetSequence.getProprietaryDataHeader() != null) + { + mLog.info("Unknown Proprietary Packet Header Type - creating unknown packet. Data Packet Format: " + + packetSequence.getPacketSequenceHeader().getDataPacketFormat() + " Proprietary Header: " + + packetSequence.getProprietaryDataHeader().getClass()); + } + return new DMRPacketMessage(packetSequence, new UnknownPacket(packet, 0), packet, packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp()); } @@ -217,8 +231,7 @@ public static IMessage createIPPacketData(PacketSequence packetSequence, Correct */ public static IMessage createDefinedShortData(PacketSequence packetSequence, CorrectedBinaryMessage packet) { - mLog.info("Unknown Short Data Packet Header Type - creating unknown packet."); - return new DMRPacketMessage(packetSequence, new UnknownPacket(packet, 0), packet, + return new DMRPacketMessage(packetSequence, new DefinedShortDataPacket(packet, 0), packet, packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp()); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/CapacityPlusServiceOptions.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/CapacityPlusServiceOptions.java index b9a13e158..84ddd55f1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/CapacityPlusServiceOptions.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/CapacityPlusServiceOptions.java @@ -83,7 +83,7 @@ public String toString() if(isInterruptible()) { - flags.add("TXI CALL"); + flags.add("INTERRUPTIBLE CALL"); //Indicates another user can interrupt this call } if(getPriority() > 0) diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/EMB.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/EMB.java index c721b668f..8e137e9fe 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/EMB.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/EMB.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -21,18 +21,16 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.dmr.message.type.LCSS; +import java.util.HashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.HashSet; - /** * DMR Voice Frame B-F Embedded Signalling Chunk */ public class EMB { private final static Logger mLog = LoggerFactory.getLogger(EMB.class); - public static final int[] CRC_CHECKSUMS = new int[]{0x02F, 0x11E, 0x1B7, 0x1E2, 0x1C9, 0x0E5, 0x073}; private static final int[] VALID_WORDS = new int[]{ 0x0000, 0x0273, 0x04E5, 0x0696, 0x09C9, 0x0BBA, 0x0D2C, 0x0F5F, 0x11E2, 0x1391, 0x1507, 0x1774, 0x182B, 0x1A58, 0x1CCE, 0x1EBD, 0x21B7, 0x23C4, 0x2552, 0x2721, 0x287E, 0x2A0D, 0x2C9B, 0x2EE8, 0x3055, 0x3226, 0x34B0, 0x36C3, 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 aebe3c07b..4e83e99e1 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,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 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 @@ -23,6 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.message.CACH; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.EmbeddedParameters; /** * DMR Voice Frames B - F @@ -35,6 +36,7 @@ public class VoiceEMBMessage extends VoiceMessage private static final int PAYLOAD_END = 172; private EMB mEMB; + private EmbeddedParameters mEmbeddedParameters; /** * DMR message frame. This message is comprised of a 24-bit prefix and a 264-bit message frame. Outbound base @@ -61,7 +63,11 @@ public String toString() sb.append(getSyncPattern().toString()); - if(getEMB().isValid() && getEMB().isEncrypted()) + if(hasEmbeddedParameters()) + { + sb.append(" ").append(getEmbeddedParameters()); + } + else if(getEMB().isValid() && getEMB().isEncrypted()) { sb.append(" ENCRYPTED"); } @@ -97,4 +103,34 @@ public BinaryMessage getFLCFragment() { return getMessage().getSubMessage(PAYLOAD_START, PAYLOAD_END); } + + /** + * Optional embedded parameters for this voice super-frame. + * @return encryption parameters or null. + */ + public EmbeddedParameters getEmbeddedParameters() + { + return mEmbeddedParameters; + } + + /** + * Sets the embedded parameters for this voice message that apply to the entire voice super-frame. + * + * These parameters are normally extracted by an external process and applied to voice frame F and the parameters + * apply to the entire voice super-frame. + * @param embeddedParameters to set + */ + public void setEmbeddedParameters(EmbeddedParameters embeddedParameters) + { + mEmbeddedParameters = embeddedParameters; + } + + /** + * Indicates if this voice message contains embedded parameters. + * @return true if it contains. + */ + public boolean hasEmbeddedParameters() + { + return mEmbeddedParameters != null; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceMessage.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceMessage.java index 1107ff73b..a092ab654 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceMessage.java @@ -1,3 +1,22 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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; @@ -5,7 +24,6 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.message.CACH; import io.github.dsheirer.module.decode.dmr.message.DMRBurst; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -15,6 +33,14 @@ */ public abstract class VoiceMessage extends DMRBurst { + /** + * Initialization vector (IV) fragments can be embedded into the AMBE voice frame for late entry to encrypted calls + * using bits 71, 67, 63, and 59 from the interleaved (ie transmitted order) voice frame. + */ + public static int[] FRAME_1_IV_FRAGMENT = new int[]{95, 91, 87, 83}; + public static int[] FRAME_2_IV_FRAGMENT = new int[]{215, 211, 207, 203}; + public static int[] FRAME_3_IV_FRAGMENT = new int[]{287, 283, 279, 275}; + /** * DMR message frame. This message is comprised of a 24-bit prefix and a 264-bit message frame. Outbound base * station frames transmit a Common Announcement Channel (CACH) in the 24-bit prefix, whereas Mobile inbound frames @@ -65,6 +91,24 @@ public List getAMBEFrames() return frames; } + /** + * Extracts the four low-order bits from the deinterleaved C3 vector of each of the three AMBE frames that carry + * fragments of the initialization vector (IV) for encrypted calls. Since we don't have access to the deinterleaved + * voice frame here, we perform delinterleaving of the 4-bit nibble using the FRAME_x_IV_FRAGMENT constants. + *

+ * See patent: https://patents.google.com/patent/EP2347540B1/en + * + * @return a three-byte array with the low-order four bits from each frame's C3 vector stored in the low nibble. + */ + public byte[] getIvFragments() + { + byte[] fragments = new byte[3]; + fragments[0] = (byte)getMessage().getInt(FRAME_1_IV_FRAGMENT); + fragments[1] = (byte)getMessage().getInt(FRAME_2_IV_FRAGMENT); + fragments[2] = (byte)getMessage().getInt(FRAME_3_IV_FRAGMENT); + return fragments; + } + @Override public boolean isValid() { diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceSuperFrameProcessor.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceSuperFrameProcessor.java new file mode 100644 index 000000000..db58f16dc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/VoiceSuperFrameProcessor.java @@ -0,0 +1,282 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.edac.BPTC_16_2; +import io.github.dsheirer.edac.Golay24; +import io.github.dsheirer.module.decode.dmr.message.IServiceOptionsProvider; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.Arc4EncryptionParameters; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.EmbeddedParameters; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.NonStandardShortBurst; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.NullShortBurst; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.ShortBurst; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.ShortBurstOpcode; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.TransmitInterrupt; +import io.github.dsheirer.module.decode.dmr.message.voice.embedded.UnknownShortBurst; + +/** + * Monitors audio call voice frame messaging to detect encrypted audio calls and extract the encryption parameters + * that are embedded into the six voice frames that comprise a voice super-frame. + *

+ * See patent: https://patents.google.com/patent/EP2347540B1/en - embedding encryption parameters in voice super frame + * See patent: https://patents.google.com/patent/US8271009B2 - TXI - interrupting voice transmissions + */ +public class VoiceSuperFrameProcessor +{ + private static final int[] IV = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 48, 49, 50, 51, 52, 53, 54, 55}; + private static final int[] CRC4 = new int[]{56, 57, 58, 59}; + private boolean mEncrypted = false; + private int mAlgorithm; + private int mKey; + private byte[] mFragmentA; + private byte[] mFragmentB; + private byte[] mFragmentC; + private byte[] mFragmentD; + private byte[] mFragmentE; + + /** + * Constructor. + */ + public VoiceSuperFrameProcessor() + { + } + + /** + * Indicates if this collector is in collecting mode. + * + * @return true if collecting. + */ + public boolean isCollecting() + { + return mEncrypted; + } + + /** + * Fully resets this collector + */ + public void reset() + { + mEncrypted = false; + softReset(); + } + + private void softReset() + { + mAlgorithm = 0; + mKey = 0; + mFragmentA = null; + mFragmentB = null; + mFragmentC = null; + mFragmentD = null; + mFragmentE = null; + } + + /** + * Indicates if the IV fragments from voice frames A-E have been collected and this is an encrypted call. + * + * @return true if the fragments are non-null. + */ + private boolean isComplete() + { + return mEncrypted && mFragmentA != null && mFragmentB != null && mFragmentC != null && mFragmentD != null && + mFragmentE != null; + } + + /** + * Process full link control message to determine if the current call sequence is encrypted. + * @param flc to inspect + */ + public void process(FullLCMessage flc) + { + if(flc instanceof IServiceOptionsProvider provider && provider.getServiceOptions().isEncrypted()) + { + mEncrypted = true; + } + } + + /** + * Processes the voice frame to extract the IV fragments and the encryption parameters. + * + * @param voiceMessage to process. + */ + public void process(VoiceMessage voiceMessage) + { + if(voiceMessage instanceof VoiceEMBMessage voiceEMB) + { + boolean valid = voiceEMB.getEMB().isValid(); + boolean encrypted = voiceEMB.getEMB().isEncrypted(); + + if(voiceEMB.getEMB().isValid() && voiceEMB.getEMB().isEncrypted()) + { + mEncrypted = true; + } + } + + switch(voiceMessage.getSyncPattern()) + { + case BASE_STATION_VOICE: + mFragmentA = voiceMessage.getIvFragments(); + break; + case BS_VOICE_FRAME_B: + mFragmentB = voiceMessage.getIvFragments(); + break; + case BS_VOICE_FRAME_C: + mFragmentC = voiceMessage.getIvFragments(); + break; + case BS_VOICE_FRAME_D: + mFragmentD = voiceMessage.getIvFragments(); + break; + case BS_VOICE_FRAME_E: + mFragmentE = voiceMessage.getIvFragments(); + break; + case BS_VOICE_FRAME_F: + if(voiceMessage instanceof VoiceEMBMessage voiceFrame6) + { + BinaryMessage frameFFragment = voiceFrame6.getFLCFragment(); + ShortBurst shortBurst = extractShortBurst(frameFFragment); + EmbeddedParameters embeddedParameters = new EmbeddedParameters(shortBurst); + + if(isComplete()) + { + String iv = extractIV(voiceMessage.getIvFragments()); + embeddedParameters.setIv(iv); + } + + voiceFrame6.setEmbeddedParameters(embeddedParameters); + } + softReset(); + break; + } + } + + /** + * Processes the short burst from the Voice Frame F FLC fragment payload and combines with the optionally available + * initialization vector to return a parameters object. + * + * @param frameFFragment containing the short-burst FLC fragment + * @param iv that was previously extracted from the voice super-frame + * @return embedded parameters. + */ + private ShortBurst extractShortBurst(BinaryMessage frameFFragment) + { + CorrectedBinaryMessage decoded = BPTC_16_2.decodeShortBurst(new CorrectedBinaryMessage(frameFFragment)); + + if(decoded == null) + { + return new NonStandardShortBurst(BPTC_16_2.deinterleave(new CorrectedBinaryMessage(frameFFragment))); + } + + ShortBurstOpcode opcode = ShortBurst.getOpcode(decoded); + return switch(opcode) + { + case NULL -> new NullShortBurst(decoded); + case ARC4_ENCRYPTION -> new Arc4EncryptionParameters(decoded); + case TXI_DELAY -> new TransmitInterrupt(decoded); + default -> new UnknownShortBurst(decoded); + }; + } + + /** + * Extracts the encryption initialization vector (IV) from the voice frame fragments, performs error detection and + * correction and if the error correction passes, returns the hex string version of the 32-bit IV. + * + * @param mFragmentF voice frame 6 IV fragments. + * @return 32-bit IV as hex string or null if the extraction process couldn't extract/correct the IV. + */ + private String extractIV(byte[] mFragmentF) + { + CorrectedBinaryMessage reassembled = new CorrectedBinaryMessage(72); + reassembled.setByte(0, combine(mFragmentA[0], mFragmentB[0])); + reassembled.setByte(8, combine(mFragmentC[0], mFragmentD[0])); + reassembled.setByte(16, combine(mFragmentE[0], mFragmentF[0])); + reassembled.setByte(24, combine(mFragmentA[1], mFragmentB[1])); + reassembled.setByte(32, combine(mFragmentC[1], mFragmentD[1])); + reassembled.setByte(40, combine(mFragmentE[1], mFragmentF[1])); + reassembled.setByte(48, combine(mFragmentA[2], mFragmentB[2])); + reassembled.setByte(56, combine(mFragmentC[2], mFragmentD[2])); + reassembled.setByte(64, combine(mFragmentE[2], mFragmentF[2])); + + int check1 = Golay24.checkAndCorrect(reassembled, 0); + int check2 = Golay24.checkAndCorrect(reassembled, 24); + int check3 = Golay24.checkAndCorrect(reassembled, 48); + + if(check1 == 2 || check2 == 2 || check3 == 2) + { + return null; + } + + int iv = reassembled.getInt(IV); + int checksum = reassembled.getInt(CRC4); + + boolean passes = crc4(iv, checksum); + + if(passes) + { + return String.format("%04X", iv); + } + else + { + return String.format("%04X", iv) + "(CRC-FAIL " + check1 + "/" + check2 + "/" + check3 + "/" + + reassembled.getCorrectedBitCount() + ")"; + } + } + + /** + * Combines the low nibble from the hi byte with the low nibble from the lo byte + * + * @param high byte containing a low order nibble + * @param low byte containing a low order nibble + * @return low order nibbles from hi and lo bytes combined. + */ + private byte combine(byte high, byte low) + { + return (byte)(((high & 0xF) << 4) | (low & 0xF)); + } + + /** + * Calculates a CRC value from the polynomial: x4 + x1 + 1 (0x13) using an initial fill of 0xF. + * @param value to calculate CRC from + * @param crc to compare + * @return true if the calculated CRC from the value matches the crc argument value. + */ + public static boolean crc4(int value, int crc) + { + long checksum = (value & 0x0FFFFFFFFl) << 4; + checksum ^= 0xF; //Initial fill + long polynomial = 0x013l << 31; + long checkBit = 0x1l << 35; + + for(int x = 31; x >= 0; x--) + { + if((checksum & checkBit) == checkBit) + { + checksum ^= polynomial; + } + polynomial >>= 1; + checkBit >>= 1; + } + + return (int)checksum == crc; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/Arc4EncryptionParameters.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/Arc4EncryptionParameters.java new file mode 100644 index 000000000..e4385b0ca --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/Arc4EncryptionParameters.java @@ -0,0 +1,80 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Encryption parameters short burst payload. + */ +public class Arc4EncryptionParameters extends ShortBurst +{ + private static final int[] KEY = new int[]{0, 1, 2, 3, 4, 5, 6, 7}; + private static final int[] ALGORITHM = new int[]{8, 9, 10}; + + /** + * Constructor + * + * @param message containing the de-interleaved and error-corrected short burst payload. + */ + public Arc4EncryptionParameters(CorrectedBinaryMessage message) + { + super(message); + } + + /** + * Encryption key ID + * @return key ID + */ + public int getKey() + { + return getMessage().getInt(KEY); + } + + /** + * Encryption algorithm + * @return algorithm (0 - 7). 1 = ARC4 + */ + public int getAlgorithmValue() + { + return getMessage().getInt(ALGORITHM); + } + + public String getAlgorithm() + { + int algorithm = getAlgorithmValue(); + + if(algorithm == 1) + { + return "EP/ARC4"; + } + + return "ALGORITHM:" + algorithm; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("ENCRYPTION:").append(getAlgorithm()); + sb.append(" KEY:").append(getKey()); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/EmbeddedParameters.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/EmbeddedParameters.java new file mode 100644 index 000000000..e9557f462 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/EmbeddedParameters.java @@ -0,0 +1,90 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +/** + * Parameters that are embedded in a DMR voice super frame. + * @param algorithm identifier + * @param key identifier + * @param iv initialization vector + */ +public class EmbeddedParameters +{ + private ShortBurst mShortBurst; + private String mIv; + + /** + * Constructor + * @param shortBurst payload + * @param iv initialization vector extracted from DMR voice super-frame + */ + public EmbeddedParameters(ShortBurst shortBurst) + { + mShortBurst = shortBurst; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(getShortBurst()); + if(hasIv()) + { + sb.append(" IV:").append(getIv()); + } + return sb.toString(); + } + + /** + * Short Burst payload message + * @return short burst + */ + public ShortBurst getShortBurst() + { + return mShortBurst; + } + + /** + * Optional initialization vector (IV) decoded from the voice super-frame + * @return iv + */ + public String getIv() + { + return mIv; + } + + /** + * Sets the optional IV value. + * @param iv to set + */ + public void setIv(String iv) + { + mIv = iv; + } + + /** + * Indicates if the optional IV is included. + * @return true if included + */ + public boolean hasIv() + { + return mIv != null; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NonStandardShortBurst.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NonStandardShortBurst.java new file mode 100644 index 000000000..dd75b0aba --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NonStandardShortBurst.java @@ -0,0 +1,44 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Short burst payload that doesn't pass the BPTC 16 fec check. + */ +public class NonStandardShortBurst extends ShortBurst +{ + /** + * Constructor + * + * @param message containing the delinterleaved and error-corrected short burst payload. + */ + public NonStandardShortBurst(CorrectedBinaryMessage message) + { + super(message); + } + + @Override + public String toString() + { + return "NON-STANDARD SHORT BURST:" + getMessage().toHexString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NullShortBurst.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NullShortBurst.java new file mode 100644 index 000000000..6d2ba5b9b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/NullShortBurst.java @@ -0,0 +1,45 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Null (empty) short burst + */ +public class NullShortBurst extends ShortBurst +{ + /** + * Constructor + * + * @param message containing the delinterleaved and error-corrected short burst payload. + */ + public NullShortBurst(CorrectedBinaryMessage message) + { + super(message); + setValid(passesCRC3()); + } + + @Override + public String toString() + { + return "NULL SHORT BURST"; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurst.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurst.java new file mode 100644 index 000000000..4d0991ba6 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurst.java @@ -0,0 +1,134 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Base class for DMR Voice Frame F Short Burst from the EMB payload. + */ +public abstract class ShortBurst +{ + private static final int[] CRC3 = new int[]{0, 1, 2}; + private static final int[] OPCODE = new int[]{8, 9, 10}; + private static final int[] FULL_MESSAGE = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + private CorrectedBinaryMessage mMessage; + private boolean mValid = true; + + /** + * Constructor + * + * @param message containing the delinterleaved and error-corrected short burst payload. + */ + public ShortBurst(CorrectedBinaryMessage message) + { + mMessage = message; + } + + /** + * Access the underlying message. + * + * @return message + */ + public CorrectedBinaryMessage getMessage() + { + return mMessage; + } + + /** + * Indicates if this message passes any CRC checks. + * @return + */ + public boolean isValid() + { + return mValid; + } + + /** + * Sets the valid CRC flag for this message + * @param valid true or false + */ + protected void setValid(boolean valid) + { + mValid = valid; + } + + /** + * Numeric opcode value. + * + * @return value. + */ + public int getOpcodeValue() + { + return getMessage().getInt(OPCODE); + } + + /** + * Opcode for this message. + * + * @return opcode + */ + public ShortBurstOpcode getOpcode() + { + return getOpcode(getMessage()); + } + + /** + * Static utility method to lookup the opcode from a short burst message. + * + * @param message containing a short burst + * @return opcode + */ + public static ShortBurstOpcode getOpcode(BinaryMessage message) + { + return ShortBurstOpcode.fromValue(message.getInt(OPCODE)); + } + + /** + * Checks the message to determine if it passes for a CRC3. Not all Short Burst messages use the CRC3 check. + * + * @return true if it passes. + */ + public boolean passesCRC3() + { + int checksum = getMessage().getInt(FULL_MESSAGE); + int polynomial = 0xB << 7; + int checkBit = 0x1 << 10; + + for(int x = 10; x >= 0; x--) + { + if(checksum == 0) + { + return true; + } + + if((checksum & checkBit) == checkBit) + { + checksum ^= polynomial; + } + polynomial >>= 1; + checkBit >>= 1; + } + + return checksum == 0; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurstOpcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurstOpcode.java new file mode 100644 index 000000000..8f9da8872 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/ShortBurstOpcode.java @@ -0,0 +1,71 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +/** + * Short Burst opcode + * + * See: https://patents.google.com/patent/US8271009B2 Page 12 + */ +public enum ShortBurstOpcode +{ + NULL(0), + ARC4_ENCRYPTION(1), + TXI_DELAY(3), + UNKNOWN(-1); + + private int mValue; + + /** + * Constructor + * @param value of the opcode + */ + ShortBurstOpcode(int value) + { + mValue = value; + } + + /** + * Numeric value for the opcode + * @return value. + */ + private int getValue() + { + return mValue; + } + + /** + * Lookup the enum entry from the specified value. + * @param value to lookup + * @return matching entry or UNKNOWN. + */ + public static ShortBurstOpcode fromValue(int value) + { + for(ShortBurstOpcode opcode: ShortBurstOpcode.values()) + { + if(opcode.getValue() == value) + { + return opcode; + } + } + + return UNKNOWN; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/TransmitInterrupt.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/TransmitInterrupt.java new file mode 100644 index 000000000..5685cf100 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/TransmitInterrupt.java @@ -0,0 +1,74 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Transmit Interrupt short burst + *

+ * See: https://patents.google.com/patent/US8271009B2 + */ +public class TransmitInterrupt extends ShortBurst +{ + private static final int[] DELAY = new int[]{3, 4, 5, 6, 7}; + + /** + * Constructor + * + * @param message containing the delinterleaved and error-corrected short burst payload. + */ + public TransmitInterrupt(CorrectedBinaryMessage message) + { + super(message); + setValid(passesCRC3()); + } + + @Override + public String toString() + { + return "TRANSMIT INTERRUPT (TXI) AT " + getDelay(); + } + + /** + * Delay to when another radio can interrupt the current call. + * @return delay string. + */ + public String getDelay() + { + int value = getMessage().getInt(DELAY); + + switch(value) + { + case 0: + return "ANY TIME"; + case 2: + return "FRAME E"; + case 4: + return "FRAME D"; + case 6: + return "FRAME C"; + case 8: + return "FRAME B"; + default: + return "UNKNOWN(" + value + ")"; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/UnknownShortBurst.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/UnknownShortBurst.java new file mode 100644 index 000000000..8aa2925ea --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/voice/embedded/UnknownShortBurst.java @@ -0,0 +1,44 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.embedded; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * Unknown or garbage short burst + */ +public class UnknownShortBurst extends ShortBurst +{ + /** + * Constructor + * + * @param message containing the delinterleaved and error-corrected short burst payload. + */ + public UnknownShortBurst(CorrectedBinaryMessage message) + { + super(message); + } + + @Override + public String toString() + { + return "UNKNOWN SHORT BURST:" + getMessage().toHexString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/DefinedShortDataPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/DefinedShortDataPacket.java new file mode 100644 index 000000000..a89ba9c15 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/DefinedShortDataPacket.java @@ -0,0 +1,98 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 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.ip; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import java.util.Collections; +import java.util.List; + +/** + * Defined Short Data Packet + */ +public class DefinedShortDataPacket implements IPacket +{ + private BinaryMessage mMessage; + private int mOffset; + + /** + * Constructor + * @param message of the complete packet + * @param offset into the message + */ + public DefinedShortDataPacket(BinaryMessage message, int offset) + { + mMessage = message; + mOffset = offset; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("DEFINED SHORT DATA PACKET:"); + + if(getMessage().size() > getOffset()) + { + sb.append(mMessage.getSubMessage(getOffset(), getMessage().size()).toHexString()); + } + else + { + sb.append("(EMPTY)"); + } + + return sb.toString(); + } + + public BinaryMessage getMessage() + { + return mMessage; + } + + public int getOffset() + { + return mOffset; + } + + @Override + public IHeader getHeader() + { + return null; + } + + @Override + public IPacket getPayload() + { + return null; + } + + @Override + public boolean hasPayload() + { + return false; + } + + @Override + public List getIdentifiers() + { + return Collections.emptyList(); + } +}