From acc970c21ceea06edd0862698755b873bd128113 Mon Sep 17 00:00:00 2001 From: Dennis Sheirer Date: Sat, 4 May 2024 18:25:32 -0400 Subject: [PATCH] #1907 Hytera GPS positions, XPT Adjacent site, and Hytera Talker Aliases. --- .../github/dsheirer/bits/BinaryMessage.java | 8 +- .../module/decode/dmr/DMRDecoderState.java | 7 +- .../message/data/csbk/CSBKMessageFactory.java | 8 +- .../decode/dmr/message/data/csbk/Opcode.java | 2 + .../csbk/hytera/HyteraXPTAdjacentSites.java | 209 ++++++++++++++++++ .../data/csbk/hytera/HyteraXPTSiteState.java | 4 +- .../dmr/message/data/lc/LCMessageFactory.java | 11 +- .../decode/dmr/message/data/lc/LCOpcode.java | 10 +- .../message/data/lc/full/GPSInformation.java | 13 +- .../data/lc/full/TalkerAliasAssembler.java | 6 +- .../data/lc/full/TalkerAliasHeader.java | 5 +- .../lc/full/hytera/HyteraXptChannelGrant.java | 119 ++++++++++ .../dmr/message/type/PositionError.java | 4 +- 13 files changed, 387 insertions(+), 19 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTAdjacentSites.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraXptChannelGrant.java diff --git a/src/main/java/io/github/dsheirer/bits/BinaryMessage.java b/src/main/java/io/github/dsheirer/bits/BinaryMessage.java index f2872447c..341798e79 100644 --- a/src/main/java/io/github/dsheirer/bits/BinaryMessage.java +++ b/src/main/java/io/github/dsheirer/bits/BinaryMessage.java @@ -507,7 +507,7 @@ public boolean hasInt(IntField intField) /** * Indicates if the message field contains a non-zero value indicated by any bits set in the int field indices. - * @param intField to inspect + * @param fragmentedField to inspect * @return true if any of the bits are set, indicating a non-zero value. */ public boolean hasInt(FragmentedIntField fragmentedField) @@ -1057,14 +1057,14 @@ public int getTwosComplement(int start, int end) if(get(start)) { //Negative value - flip and add one - BinaryMessage fragment = getSubMessage(start, end); + BinaryMessage fragment = getSubMessage(start, end + 1); fragment.flip(0, fragment.size()); - return fragment.getInt(1, fragment.size()) + 1; + return -(fragment.getInt(0, fragment.size() - 1) + 1); } else { //Positive value - return the contents. - return getInt(start + 1, end); + return getInt(start, end); } } 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 4b939c29b..1d019c8d2 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 @@ -1329,14 +1329,15 @@ private void processLinkControl(LCMessage message, boolean isTerminator) } break; case FULL_STANDARD_GPS_INFO: - if(message instanceof GPSInformation gpsInformation) + case FULL_HYTERA_GPS_INFO: + if(message instanceof GPSInformation gps) { PlottableDecodeEvent plottableGPS = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, message.getTimestamp()) .channel(getCurrentChannel()) - .details("LOCATION:" + gpsInformation.getGPSLocation()) + .details("LOCATION:" + gps.getGPSLocation()) .identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers())) .protocol(Protocol.DMR) - .location(gpsInformation.getPosition()) + .location(gps.getPosition()) .build(); broadcast(plottableGPS); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/CSBKMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/CSBKMessageFactory.java index 7d638cd39..9575de805 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/CSBKMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/CSBKMessageFactory.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2023 Dennis Sheirer + * 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 @@ -31,11 +31,12 @@ import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko44; import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraSmsAvailableNotification; import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraTrafficChannelTalkerStatus; +import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTAdjacentSites; import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTPreamble; import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTSiteState; +import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAdvantageModeVoiceChannelUpdate; import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAloha; import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxOpenModeVoiceChannelUpdate; -import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAdvantageModeVoiceChannelUpdate; import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusCSBKO_60; import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusDataRevertWindowAnnouncement; import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusDataRevertWindowGrant; @@ -246,6 +247,9 @@ public static CSBKMessage create(DMRSyncPattern pattern, CorrectedBinaryMessage case HYTERA_68_XPT_SITE_STATE: csbk = new HyteraXPTSiteState(pattern, message, cach, slotType, timestamp, timeslot); break; + case HYTERA_68_XPT_ADJACENT_SITE: + csbk = new HyteraXPTAdjacentSites(pattern, message, cach, slotType, timestamp, timeslot); + break; case HYTERA_08_ACKNOWLEDGE: csbk = new Hytera08Acknowledge(pattern, message, cach, slotType, timestamp, timeslot); break; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java index 7e8b0f382..28d0af01a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java @@ -98,6 +98,8 @@ public enum Opcode HYTERA_08_TRAFFIC_CHANNEL_TALKER_STATUS(Vendor.HYTERA_8, 47, "HYTERA 08 CSBKO 47"), HYTERA_68_XPT_SITE_STATE(Vendor.HYTERA_68, 10, "HYTERA 68 XPT SITE STATE"), + HYTERA_68_XPT_ADJACENT_SITE(Vendor.HYTERA_68, 11, "HYTERA 68 XPT ADJACENT SITE"), + HYTERA_68_ALOHA(Vendor.HYTERA_68, 25, "HYTERA 68 ALOHA"), HYTERA_68_ACKNOWLEDGE(Vendor.HYTERA_68, 32, "HYTERA 68 ACKNOWLEDGE"), HYTERA_68_ANNOUNCEMENT(Vendor.HYTERA_68, 40, "HYTERA 68 ANNOUNCEMENT"), diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTAdjacentSites.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTAdjacentSites.java new file mode 100644 index 000000000..4db183816 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTAdjacentSites.java @@ -0,0 +1,209 @@ +/* + * ***************************************************************************** + * 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.dmr.message.data.csbk.hytera; + +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.message.CACH; +import io.github.dsheirer.module.decode.dmr.message.data.SlotType; +import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage; +import java.util.Collections; +import java.util.List; + +/** + * Hytera XPT - Adjacent Site State + */ +public class HyteraXPTAdjacentSites extends CSBKMessage +{ + private static final int[] SEQUENCE_NUMBER = new int[]{0, 1}; + private static final int[] SITE_1 = new int[]{16, 17, 18, 19, 20}; //Unknown: 21-23 + private static final int[] FREE_1 = new int[]{24, 25, 26, 27}; //Unknown: 28-31 + private static final int[] SITE_2 = new int[]{32, 33, 34, 35, 36}; //Unknown: 37-39 + private static final int[] FREE_2 = new int[]{40, 41, 42, 43}; //Unknown: 44-47 + private static final int[] SITE_3 = new int[]{48, 49, 50, 51, 52}; //Unknown: 53-55 + private static final int[] FREE_3 = new int[]{56, 57, 58, 59}; //Unknown: 60-63 + private static final int[] SITE_4 = new int[]{64, 65, 66, 67, 68}; //Unknown: 69-71 + private static final int[] FREE_4 = new int[]{72, 73, 74, 75}; //Unknown: 76-79 + + /** + * Constructs an instance + * + * @param syncPattern for the CSBK + * @param message bits + * @param cach for the DMR burst + * @param slotType for this message + * @param timestamp + * @param timeslot + */ + public HyteraXPTAdjacentSites(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot) + { + super(syncPattern, message, cach, slotType, timestamp, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("[CRC-ERROR] "); + } + + sb.append("CC:").append(getSlotType().getColorCode()); + if(hasRAS()) + { + sb.append(" RAS:").append(getBPTCReservedBits()); + } + + sb.append(" HYTERA XPT NEIGHBORS"); + + if(hasSite1()) + { + sb.append(" SITE:").append(getSite1()); + sb.append(" FREE:").append(getFree1()); + } + + if(hasSite2()) + { + sb.append(" SITE:").append(getSite2()); + sb.append(" FREE:").append(getFree2()); + } + + if(hasSite3()) + { + sb.append(" SITE:").append(getSite3()); + sb.append(" FREE:").append(getFree3()); + } + + if(hasSite4()) + { + sb.append(" SITE:").append(getSite4()); + sb.append(" FREE:").append(getFree4()); + } + + sb.append(" MSG:").append(getMessage().toHexString()); + + return sb.toString(); + } + + /** + * Site ID for site number 1. + */ + public int getSite1() + { + return getMessage().getInt(SITE_1); + } + + /** + * Indicates if site number 1 is valid (ie non-zero). + */ + public boolean hasSite1() + { + return getSite1() > 0; + } + + /** + * Free repeater for site 1. + */ + public int getFree1() + { + return getMessage().getInt(FREE_1); + } + + /** + * Site ID for site number 2. + */ + public int getSite2() + { + return getMessage().getInt(SITE_2); + } + + /** + * Indicates if site number 2 is valid (ie non-zero). + */ + public boolean hasSite2() + { + return getSite2() > 0; + } + + /** + * Free repeater for site 2. + */ + public int getFree2() + { + return getMessage().getInt(FREE_2); + } + + /** + * Site ID for site number 3. + */ + public int getSite3() + { + return getMessage().getInt(SITE_3); + } + + /** + * Indicates if site number 3 is valid (ie non-zero). + */ + public boolean hasSite3() + { + return getSite3() > 0; + } + + /** + * Free repeater for site 3. + */ + public int getFree3() + { + return getMessage().getInt(FREE_3); + } + + /** + * Site ID for site number 4. + */ + public int getSite4() + { + return getMessage().getInt(SITE_4); + } + + /** + * Indicates if site number 4 is valid (ie non-zero). + */ + public boolean hasSite4() + { + return getSite4() > 0; + } + + /** + * Free repeater for site 4. + */ + public int getFree4() + { + return getMessage().getInt(FREE_4); + } + + @Override + public List getIdentifiers() + { + return Collections.emptyList(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTSiteState.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTSiteState.java index 26d04c781..dc96bf4c0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTSiteState.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/hytera/HyteraXPTSiteState.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2023 Dennis Sheirer + * 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 @@ -237,7 +237,7 @@ public String toString() { sb.append(" RAS:").append(getBPTCReservedBits()); } - sb.append(" HYTERA XPT SITE "); + sb.append(" HYTERA XPT SITE"); if(isAllChannelsBusy()) { 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 18922dedb..a65dd2c68 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 @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2023 Dennis Sheirer + * 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 @@ -37,6 +37,7 @@ 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.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.hytera.HyteraXptChannelGrant; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityMaxTalkerAlias; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityMaxTalkerAliasContinuation; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityMaxVoiceChannelUser; @@ -132,6 +133,7 @@ else if(message.size() == 96) flc = new UnitToUnitVoiceChannelUser(message, timestamp, timeslot); break; case FULL_STANDARD_GPS_INFO: + case FULL_HYTERA_GPS_INFO: flc = new GPSInformation(message, timestamp, timeslot); break; case FULL_STANDARD_TERMINATOR_DATA: @@ -169,17 +171,24 @@ else if(message.size() == 96) flc = new HyteraTerminator(message, timestamp, timeslot); break; case FULL_STANDARD_TALKER_ALIAS_HEADER: + case FULL_HYTERA_TALKER_ALIAS_HEADER: flc = new TalkerAliasHeader(message, timestamp, timeslot); break; case FULL_STANDARD_TALKER_ALIAS_BLOCK_1: + case FULL_HYTERA_TALKER_ALIAS_BLOCK_1: flc = new TalkerAliasBlock1(message, timestamp, timeslot); break; case FULL_STANDARD_TALKER_ALIAS_BLOCK_2: + case FULL_HYTERA_TALKER_ALIAS_BLOCK_2: flc = new TalkerAliasBlock2(message, timestamp, timeslot); break; case FULL_STANDARD_TALKER_ALIAS_BLOCK_3: + case FULL_HYTERA_TALKER_ALIAS_BLOCK_3: flc = new TalkerAliasBlock3(message, timestamp, timeslot); break; + case FULL_HYTERA_XPT_CHANNEL_GRANT: + flc = new HyteraXptChannelGrant(message, timestamp, timeslot); + break; default: flc = new UnknownFullLCMessage(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 a90e33acc..f621d18bc 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 @@ -62,6 +62,12 @@ public enum LCOpcode FULL_HYTERA_GROUP_VOICE_CHANNEL_USER(Vendor.HYTERA_68, true, 0, "HYTERA GROUP VOICE CHANNEL USER"), FULL_HYTERA_UNIT_TO_UNIT_VOICE_CHANNEL_USER(Vendor.HYTERA_68, true, 3, "HYTERA UNIT-TO-UNIT VOICE CHANNEL USER"), + FULL_HYTERA_TALKER_ALIAS_HEADER(Vendor.HYTERA_68,true, 4, "HYTERA TALKER ALIAS HEADER"), + FULL_HYTERA_TALKER_ALIAS_BLOCK_1(Vendor.HYTERA_68,true, 5, "HYTERA TALKER ALIAS BLOCK 1"), + FULL_HYTERA_TALKER_ALIAS_BLOCK_2(Vendor.HYTERA_68,true, 6, "HYTERA TALKER ALIAS BLOCK 2"), + FULL_HYTERA_TALKER_ALIAS_BLOCK_3(Vendor.HYTERA_68,true, 7, "HYTERA TALKER ALIAS BLOCK 3"), + FULL_HYTERA_GPS_INFO(Vendor.HYTERA_68,true, 8, "HYTERA GPS INFO"), + FULL_HYTERA_XPT_CHANNEL_GRANT(Vendor.HYTERA_68, true, 9, "HYTERA XPT CHANNEL GRANT"), FULL_HYTERA_TERMINATOR(Vendor.HYTERA_68, true, 48, "HYTERA TERMINATOR"), SHORT_STANDARD_NULL_MESSAGE(Vendor.STANDARD,false,0, "NULL MESSAGE"), @@ -82,7 +88,9 @@ public enum LCOpcode SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "UNKNOWN"); private static final EnumSet TALKER_ALIAS_OPCODES = EnumSet.of(FULL_STANDARD_TALKER_ALIAS_HEADER, - FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3); + FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3, + FULL_HYTERA_TALKER_ALIAS_HEADER, FULL_HYTERA_TALKER_ALIAS_BLOCK_1, FULL_HYTERA_TALKER_ALIAS_BLOCK_2, + FULL_HYTERA_TALKER_ALIAS_BLOCK_3); private final Vendor mVendor; private final boolean mFull; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/GPSInformation.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/GPSInformation.java index 823aee981..8d91a1fde 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/GPSInformation.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/GPSInformation.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2023 Dennis Sheirer + * 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 @@ -19,6 +19,7 @@ package io.github.dsheirer.module.decode.dmr.message.data.lc.full; +import io.github.dsheirer.bits.BinaryMessage; import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.location.LocationIdentifier; @@ -67,9 +68,10 @@ public String toString() sb.append("[CRC-ERROR] "); } - sb.append("GPS LOCATION "); + sb.append("FLC GPS LOCATION "); sb.append(getGPSLocation().toString()); sb.append(" POSITION ERROR:").append(getPositionError().toString()); + sb.append(" MSG:").append(getMessage().toHexString()); return sb.toString(); } @@ -130,4 +132,11 @@ public List getIdentifiers() return mIdentifiers; } + + public static void main(String[] args) + { + CorrectedBinaryMessage cbm = new CorrectedBinaryMessage(BinaryMessage.loadHex("08000F8A177E3903C230")); + GPSInformation gps = new GPSInformation(cbm, 0, 1); + System.out.println(gps); + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasAssembler.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasAssembler.java index 1629b59de..91c7d8df3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasAssembler.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasAssembler.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2023 Dennis Sheirer + * 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 @@ -83,6 +83,7 @@ public TalkerAliasComplete process(FullLCMessage message) switch(message.getOpcode()) { case FULL_STANDARD_TALKER_ALIAS_HEADER: + case FULL_HYTERA_TALKER_ALIAS_HEADER: if(message instanceof TalkerAliasHeader tah) { if(message.getTimeslot() == 1) @@ -99,6 +100,7 @@ public TalkerAliasComplete process(FullLCMessage message) } } case FULL_STANDARD_TALKER_ALIAS_BLOCK_1: + case FULL_HYTERA_TALKER_ALIAS_BLOCK_1: if(message instanceof TalkerAliasBlock1 tab1) { if(message.getTimeslot() == 1) @@ -115,6 +117,7 @@ public TalkerAliasComplete process(FullLCMessage message) } } case FULL_STANDARD_TALKER_ALIAS_BLOCK_2: + case FULL_HYTERA_TALKER_ALIAS_BLOCK_2: if(message instanceof TalkerAliasBlock2 tab2) { if(message.getTimeslot() == 1) @@ -131,6 +134,7 @@ public TalkerAliasComplete process(FullLCMessage message) } } case FULL_STANDARD_TALKER_ALIAS_BLOCK_3: + case FULL_HYTERA_TALKER_ALIAS_BLOCK_3: if(message instanceof TalkerAliasBlock3 tab3) { if(message.getTimeslot() == 1) diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasHeader.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasHeader.java index 0a0433250..39952c817 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasHeader.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/TalkerAliasHeader.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2023 Dennis Sheirer + * 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 @@ -70,6 +70,9 @@ public int getCharacterLength() */ public int getTotalBitLength() { + TalkerAliasDataFormat format = getFormat(); + int bitsPerCharacter = format.getBitsPerCharacter(); + int characterLength = getCharacterLength(); int total = getFormat().getBitsPerCharacter() * getCharacterLength(); //Max payload bit length is 217 (49 + 56 + 56 + 56) diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraXptChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraXptChannelGrant.java new file mode 100644 index 000000000..b3d3552c0 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/hytera/HyteraXptChannelGrant.java @@ -0,0 +1,119 @@ +/* + * ***************************************************************************** + * 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.dmr.message.data.lc.full.hytera; + +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 io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; +import java.util.ArrayList; +import java.util.List; + +/** + * Hytera XPT Channel Grant + * + * 49 68 50 100034 002AF9 1A770C + */ +public class HyteraXptChannelGrant extends FullLCMessage +{ + private static final int[] CHANNEL = new int[]{16, 17, 18, 19}; + //Unknown: 20-23 + private static final int[] TARGET = 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 = 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 RadioIdentifier mSourceRadio; + private TalkgroupIdentifier mTalkgroup; + private List mIdentifiers; + + /** + * Constructs an instance. + * + * @param message for the link control payload + */ + public HyteraXptChannelGrant(CorrectedBinaryMessage message, long timestamp, int timeslot) + { + super(message, timestamp, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + + if(!isValid()) + { + sb.append("[CRC-ERROR] "); + } + + sb.append("FLC HYTERA XPT CHANNEL GRANT FM:"); + sb.append(getSourceRadio()); + sb.append(" TO:").append(getTalkgroup()); + sb.append(" CHANNEL:").append(getChannel()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + public int getChannel() + { + return getMessage().getInt(CHANNEL); + } + + /** + * Talkgroup address + */ + public TalkgroupIdentifier getTalkgroup() + { + if(mTalkgroup == null) + { + mTalkgroup = DMRTalkgroup.create(getMessage().getInt(TARGET)); + } + + return mTalkgroup; + } + + /** + * Source radio address + */ + public RadioIdentifier getSourceRadio() + { + if(mSourceRadio == null) + { + mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE)); + } + + return mSourceRadio; + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getTalkgroup()); + mIdentifiers.add(getSourceRadio()); + } + + return mIdentifiers; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PositionError.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PositionError.java index 89f4e42c3..5b366fcda 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PositionError.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/PositionError.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * 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 @@ -33,7 +33,7 @@ public enum PositionError ERROR_4("LESS THAN 20 KILOMETERS"), ERROR_5("LESS THAN 200 KILOMETERS"), ERROR_6("MORE THAN 200 KILOMETERS"), - UNKNOWN("UNKNOWN"); + UNKNOWN("NOT KNOWN"); private String mLabel;