From c0c836844037e8bb97a428718452c50d12da9fe3 Mon Sep 17 00:00:00 2001 From: Dennis Sheirer Date: Sat, 11 May 2024 03:53:07 -0400 Subject: [PATCH] #1914 corrections to assembled talker alias for logging. --- .../p25/phase1/P25P1MessageProcessor.java | 4 +- .../phase1/message/lc/LinkControlWord.java | 14 +- ...va => LCMotorolaTalkerAliasAssembler.java} | 12 +- .../LCMotorolaTalkerAliasComplete.java | 58 +++---- .../MotorolaTalkerAliasAssembler.java | 147 ++++++++++++++++++ .../motorola/MotorolaTalkerAliasComplete.java | 92 +++++++++++ .../motorola/MotorolaTalkerAliasHeader.java | 5 +- 7 files changed, 286 insertions(+), 46 deletions(-) rename src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/{MotorolaTalkerAliasAssemblerP25Phase1.java => LCMotorolaTalkerAliasAssembler.java} (93%) create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasAssembler.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasComplete.java diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java index 5cb3a5e00..c7798b8f4 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageProcessor.java @@ -25,7 +25,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBandReceiver; import io.github.dsheirer.module.decode.p25.phase1.message.lc.ExtendedSourceLinkControlWord; import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord; -import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.MotorolaTalkerAliasAssemblerP25Phase1; +import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkerAliasAssembler; import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension; import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message; import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage; @@ -64,7 +64,7 @@ public class P25P1MessageProcessor implements Listener /** * Motorola talker alias assembler for link control header and data blocks. */ - private MotorolaTalkerAliasAssemblerP25Phase1 mMotorolaTalkerAliasAssembler = new MotorolaTalkerAliasAssemblerP25Phase1(); + private LCMotorolaTalkerAliasAssembler mMotorolaTalkerAliasAssembler = new LCMotorolaTalkerAliasAssembler(); /** * Constructs an instance diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWord.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWord.java index 3246bac00..24247f8cd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWord.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWord.java @@ -44,20 +44,20 @@ public abstract class LinkControlWord extends AbstractMessage public static final int OCTET_7_BIT_56 = 56; public static final int OCTET_8_BIT_64 = 64; public static final int OCTET_9_BIT_72 = 72; - public static final int OCTET_10_BIT_80 = 80; public static final int OCTET_11_BIT_88 = 88; - public static final int OCTET_12_BIT_96 = 96; public static final int OCTET_13_BIT_104 = 104; - public static final int OCTET_14_BIT_112 = 112; public static final int OCTET_15_BIT_120 = 120; - public static final int OCTET_16_BIT_128 = 128; public static final int OCTET_17_BIT_136 = 136; - public static final int OCTET_18_BIT_144 = 144; public static final int OCTET_19_BIT_152 = 152; - public static final int OCTET_20_BIT_160 = 160; public static final int OCTET_21_BIT_168 = 168; - public static final int OCTET_22_BIT_176 = 176; public static final int OCTET_23_BIT_184 = 184; + public static final int OCTET_25_BIT_200 = 200; + public static final int OCTET_27_BIT_216 = 216; + public static final int OCTET_29_BIT_232 = 232; + public static final int OCTET_31_BIT_248 = 248; + public static final int OCTET_33_BIT_264 = 264; + public static final int OCTET_35_BIT_280 = 280; + public static final int OCTET_37_BIT_296 = 296; private static final int ENCRYPTION_FLAG = 0; private static final int STANDARD_VENDOR_ID_FLAG = 1; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/MotorolaTalkerAliasAssemblerP25Phase1.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasAssembler.java similarity index 93% rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/MotorolaTalkerAliasAssemblerP25Phase1.java rename to src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasAssembler.java index f08063847..8206d596d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/MotorolaTalkerAliasAssemblerP25Phase1.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasAssembler.java @@ -25,22 +25,24 @@ import java.util.Map; /** - * Reassembles a P25 Motorola talker alias from a Header and Data Blocks from a Phase 1 traffic channel. + * Reassembles a P25 Motorola talker alias from a Header and Data Blocks. * - * This is used for traffic channels and monitors the link control messaging stream. + * This is used for traffic channels and monitors: + * - Phase 1: link control messaging stream. + * - Phase 2: mac messaging */ -public class MotorolaTalkerAliasAssemblerP25Phase1 +public class LCMotorolaTalkerAliasAssembler { private static final int DATA_BLOCK_FRAGMENT_LENGTH = 44; private LCMotorolaTalkerAliasHeader mHeader; - private Map mDataBlocks = new HashMap<>(); + private Map mDataBlocks = new HashMap<>(); private int mSequence = -1; private long mMostRecentTimestamp; /** * Constructs an instance */ - public MotorolaTalkerAliasAssemblerP25Phase1() + public LCMotorolaTalkerAliasAssembler() { } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasComplete.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasComplete.java index d997f77c9..98d6ab1f3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasComplete.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaTalkerAliasComplete.java @@ -45,20 +45,20 @@ public class LCMotorolaTalkerAliasComplete extends LinkControlWord implements IM private static final IntField SUID_SYSTEM = IntField.length12(OCTET_2_BIT_16 + 4); private static final IntField SUID_ID = IntField.length24(OCTET_4_BIT_32); private static final int ENCODED_ALIAS_START = OCTET_7_BIT_56; - private static final IntField CHUNK_2 = IntField.length16(OCTET_8_BIT_64); - private static final IntField CHUNK_4 = IntField.length16(OCTET_10_BIT_80); - private static final IntField CHUNK_5 = IntField.length16(OCTET_11_BIT_88); - private static final IntField CHUNK_6 = IntField.length16(OCTET_12_BIT_96); - private static final IntField CHUNK_7 = IntField.length16(OCTET_13_BIT_104); - private static final IntField CHUNK_8 = IntField.length16(OCTET_14_BIT_112); - private static final IntField CHUNK_9 = IntField.length16(OCTET_15_BIT_120); - private static final IntField CHUNK_10 = IntField.length16(OCTET_16_BIT_128); - private static final IntField CHUNK_11 = IntField.length16(OCTET_17_BIT_136); - private static final IntField CHUNK_12 = IntField.length16(OCTET_18_BIT_144); - private static final IntField CHUNK_13 = IntField.length16(OCTET_19_BIT_152); - private static final IntField CHUNK_15 = IntField.length16(OCTET_21_BIT_168); - private static final IntField CHUNK_16 = IntField.length16(OCTET_22_BIT_176); - private static final IntField CHUNK_17 = IntField.length16(OCTET_23_BIT_184); + private static final IntField CHUNK_2 = IntField.length16(OCTET_9_BIT_72); + private static final IntField CHUNK_4 = IntField.length16(OCTET_13_BIT_104); + private static final IntField CHUNK_5 = IntField.length16(OCTET_15_BIT_120); + private static final IntField CHUNK_6 = IntField.length16(OCTET_17_BIT_136); + private static final IntField CHUNK_7 = IntField.length16(OCTET_19_BIT_152); + private static final IntField CHUNK_8 = IntField.length16(OCTET_21_BIT_168); + private static final IntField CHUNK_9 = IntField.length16(OCTET_23_BIT_184); + private static final IntField CHUNK_10 = IntField.length16(OCTET_25_BIT_200); + private static final IntField CHUNK_11 = IntField.length16(OCTET_27_BIT_216); + private static final IntField CHUNK_12 = IntField.length16(OCTET_29_BIT_232); + private static final IntField CHUNK_13 = IntField.length16(OCTET_31_BIT_248); + private static final IntField CHUNK_15 = IntField.length16(OCTET_33_BIT_264); + private static final IntField CHUNK_16 = IntField.length16(OCTET_35_BIT_280); + private static final IntField CHUNK_17 = IntField.length16(OCTET_37_BIT_296); private APCO25Talkgroup mTalkgroup; private APCO25FullyQualifiedRadioIdentifier mRadio; @@ -86,6 +86,20 @@ public LCMotorolaTalkerAliasComplete(CorrectedBinaryMessage message, APCO25Talkg mTimestamp = timestamp; } + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("MOTOROLA TALKER ALIAS COMPLETE"); + sb.append(" RADIO:").append(getRadio()); + sb.append(" TG:").append(getTalkgroup()); + sb.append(" ENCODED:").append(getEncodedAlias().toHexString()); + sb.append(" ALIAS:").append(getAlias()); + sb.append(" DATA BLOCKS:").append(mDataBlockCount); + sb.append(" SEQUENCE:").append(mSequence); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + @Override public LinkControlOpcode getOpcode() { @@ -133,20 +147,6 @@ public int getSequence() return mSequence; } - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append("MOTOROLA TALKER ALIAS COMPLETE"); - sb.append(" TG:").append(getTalkgroup()); - sb.append(" RADIO:").append(getRadio()); - sb.append(" ENCODED:").append(getEncodedAlias().toHexString()); - sb.append(" ALIAS:").append(getAlias()); - sb.append(" DATA BLOCKS:").append(mDataBlockCount); - sb.append(" SEQUENCE:").append(mSequence); - sb.append(" MSG:").append(getMessage().toHexString()); - return sb.toString(); - } - public P25TalkerAliasIdentifier getAlias() { if(mAlias == null) @@ -266,7 +266,7 @@ else if(getInt(CHUNK_15) > 0) break; } - return getMessage().getSubMessage(ENCODED_ALIAS_START, length * 16 + 1); + return getMessage().getSubMessage(ENCODED_ALIAS_START, ENCODED_ALIAS_START + (length * 16)); } /** diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasAssembler.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasAssembler.java new file mode 100644 index 000000000..e35ae5f7d --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasAssembler.java @@ -0,0 +1,147 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2024 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.p25.phase2.message.mac.structure.motorola; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkerAliasComplete; +import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructure; +import java.util.HashMap; +import java.util.Map; + +/** + * Reassembles a P25 Motorola talker alias from a Header and Data Blocks. + * + * This is used for traffic channels and monitors: + * - Phase 2: mac messaging + */ +public class MotorolaTalkerAliasAssembler +{ + private static final int DATA_BLOCK_FRAGMENT_LENGTH = 44; + private MotorolaTalkerAliasHeader mHeader; + private Map mDataBlocks = new HashMap<>(); + private int mSequence = -1; + private long mMostRecentTimestamp; + + /** + * Constructs an instance + */ + public MotorolaTalkerAliasAssembler() + { + } + + /** + * Link control word to process. + * @param mac to add + * @return true if we can (now) assemble a complete talker alias from the header and data blocks. + */ + public boolean add(MacStructure mac, long timestamp) + { + if(mac instanceof MotorolaTalkerAliasHeader header) + { + mMostRecentTimestamp = timestamp; + + if(mSequence != header.getSequence()) + { + mDataBlocks.clear(); + mSequence = header.getSequence(); + } + + mHeader = header; + } + else if(mac instanceof MotorolaTalkerAliasDataBlock block) + { + mMostRecentTimestamp = timestamp; + + if(block.getSequence() != mSequence) + { + mHeader = null; + mDataBlocks.clear(); + mSequence = block.getSequence(); + } + + mDataBlocks.put(block.getBlockNumber(), block); + } + else + { + return false; //For all lcw's that are not headers or data blocks + } + + return isComplete(); + } + + /** + * Indicates if the assembler has a header and the correct number of data blocks to reassemble an alias. + */ + private boolean isComplete() + { + if(mHeader != null) + { + int dataBlockCount = mHeader.getBlockCount(); + + for(int x = 1; x <= dataBlockCount; x++) + { + if(!mDataBlocks.containsKey(x)) + { + return false; + } + } + + return true; + } + + return false; + } + + /** + * Assembles the talker alias once the header and data blocks have been collected. + * + * Note: do not invoke unless the add() methods indicate that the assembler is complete. + * @return assembled talker alias + * @throws IllegalStateException if the assembler can't assemble the alias. + */ + public LCMotorolaTalkerAliasComplete assemble() throws IllegalStateException + { + if(!isComplete()) + { + throw new IllegalStateException("Can't assemble talker alias - missing header or data block(s)"); + } + + int dataBlockCount = mHeader.getBlockCount(); + CorrectedBinaryMessage reassembled = new CorrectedBinaryMessage(dataBlockCount * DATA_BLOCK_FRAGMENT_LENGTH); + + int offset = 0; + + for(int x = 1; x <= dataBlockCount; x++) + { + MotorolaTalkerAliasDataBlock block = mDataBlocks.get(x); + reassembled.load(offset, block.getFragment()); + offset += DATA_BLOCK_FRAGMENT_LENGTH; + } + + LCMotorolaTalkerAliasComplete complete = new LCMotorolaTalkerAliasComplete(reassembled, mHeader.getTalkgroup(), + dataBlockCount, mHeader.getSequence(), mMostRecentTimestamp); + + mHeader = null; + mDataBlocks.clear(); + + System.out.println(complete); + return complete; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasComplete.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasComplete.java new file mode 100644 index 000000000..4edbe8872 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasComplete.java @@ -0,0 +1,92 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2024 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.p25.phase2.message.mac.structure.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.bits.IntField; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructure; +import java.util.Collections; +import java.util.List; + +/** + * Motorola (reassembled) talker alias - complete. + */ +public class MotorolaTalkerAliasComplete extends MacStructure +{ +//TODO: fix these + private static final IntField BLOCK_NUMBER = IntField.length8(OCTET_2_BIT_8); + private static final IntField SEQUENCE = IntField.length4(OCTET_3_BIT_16); + private static final int FRAGMENT_START = OCTET_3_BIT_16 + 4; //inclusive + private static final int FRAGMENT_END = OCTET_10_BIT_72; //exclusive + + /** + * Constructs a Link Control Word from the binary message sequence. + * + * @param message + */ + public MotorolaTalkerAliasComplete(CorrectedBinaryMessage message) + { + super(message, 0); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("MOTOROLA TALKER ALIAS DATA BLOCK:").append(getBlockNumber()); + sb.append(" OF SEQUENCE:").append(getSequence()); + sb.append(" FRAGMENT:").append(getFragment().toHexString()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + /** + * Fragment of encoded alias. + */ + public BinaryMessage getFragment() + { + return getMessage().getSubMessage(getOffset() + FRAGMENT_START, getOffset() + FRAGMENT_END); + } + + /** + * Block number + */ + public int getBlockNumber() + { + return getInt(BLOCK_NUMBER); + } + + /** + * Sequence number that identifies the header and data blocks as all part of the same sequence. + */ + public int getSequence() + { + return getInt(SEQUENCE); + } + + + @Override + public List getIdentifiers() + { + return Collections.emptyList(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasHeader.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasHeader.java index 10e7279a5..894fcb2e1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/structure/motorola/MotorolaTalkerAliasHeader.java @@ -23,7 +23,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.bits.IntField; import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier; import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25FullyQualifiedRadioIdentifier; import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructureVendor; @@ -46,7 +45,7 @@ public class MotorolaTalkerAliasHeader extends MacStructureVendor private static final int FRAGMENT_START = OCTET_10_BIT_72; private static final int FRAGMENT_END = OCTET_18_BIT_136; private List mIdentifiers; - private TalkgroupIdentifier mTalkgroup; + private APCO25Talkgroup mTalkgroup; private APCO25FullyQualifiedRadioIdentifier mRadio; /** @@ -121,7 +120,7 @@ public int getBlockCount() /** * Talkgroup identifier */ - public TalkgroupIdentifier getTalkgroup() + public APCO25Talkgroup getTalkgroup() { if(mTalkgroup == null) {