#1607 Add support for L3Harris dynamic regroup/patch messages and events. Includes parsing for L3Harris unknown opcodes 10, 42 and 43.

This commit is contained in:
sheirerd
2023-10-17 06:55:20 -04:00
committed by Dennis Sheirer
parent 123c25b142
commit 215737ad9b
28 changed files with 1186 additions and 231 deletions

View File

@@ -95,6 +95,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.isp.UMBTCTe
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp.HarrisGroupRegroupExplicitEncryptionCommand;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.motorola.osp.MotorolaDenyResponse;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrantUpdate;
@@ -1222,6 +1223,21 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
mPatchGroupManager.removePatchGroups(tsbk.getIdentifiers());
break;
//L3HARRIS PATCH GROUP OPCODES
case HARRIS_OSP_GRG_EXENC_CMD:
if(tsbk instanceof HarrisGroupRegroupExplicitEncryptionCommand regroup)
{
if(regroup.getRegroupOptions().isActivate())
{
mPatchGroupManager.addPatchGroup(regroup.getPatchGroup());
}
else
{
mPatchGroupManager.removePatchGroup(regroup.getPatchGroup());
}
}
break;
//STANDARD - INBOUND OPCODES
case ISP_GROUP_VOICE_SERVICE_REQUEST:
if(tsbk instanceof GroupVoiceServiceRequest)

View File

@@ -42,6 +42,7 @@ public class TerminatorMessageFilterSet extends FilterSet<IMessage>
addFilter(new TerminatorMessageFilter("Command/Status", LinkControlOpcode.COMMAND_STATUS_OPCODES));
addFilter(new TerminatorMessageFilter("Network/Channel", LinkControlOpcode.NETWORK_OPCODES));
addFilter(new TerminatorMessageFilter("Voice", LinkControlOpcode.VOICE_OPCODES));
addFilter(new TerminatorMessageFilter("L3Harris", LinkControlOpcode.L3HARRIS_OPCODES));
addFilter(new TerminatorMessageFilter("Motorola", LinkControlOpcode.MOTOROLA_OPCODES));
List<LinkControlOpcode> others = new ArrayList<>();

View File

@@ -98,7 +98,12 @@ public enum LinkControlOpcode
MOTOROLA_PATCH_GROUP_DELETE("PATCH GROUP DELETE", 4),
MOTOROLA_UNIT_GPS("UNIT GPS", 6),
MOTOROLA_TALK_COMPLETE("TALK_COMPLETE", 15),
MOTOROLA_UNKNOWN("UNKNOWN", -1),
MOTOROLA_UNKNOWN("MOTOROLA UNKNOWN", -1),
L3HARRIS_UNKNOWN_0A("UNKNOWN OPCODE 10", 10),
L3HARRIS_UNKNOWN_2A("UNKNOWN OPCODE 42", 42),
L3HARRIS_UNKNOWN_2B("UNKNOWN OPCODE 43", 43),
L3HARRIS_UNKNOWN("L3HARRIS UNKNOWN", -1),
UNKNOWN("UNKNOWN", -1);
@@ -130,6 +135,11 @@ public enum LinkControlOpcode
public static final EnumSet<LinkControlOpcode> MOTOROLA_OPCODES =
EnumSet.range(MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER, MOTOROLA_UNKNOWN);
/**
* L3Harris Opcodes
*/
public static final EnumSet<LinkControlOpcode> L3HARRIS_OPCODES = EnumSet.of(L3HARRIS_UNKNOWN_0A,
L3HARRIS_UNKNOWN_2A, L3HARRIS_UNKNOWN_2B, L3HARRIS_UNKNOWN);
/**
* Network/channel related opcodes
@@ -191,6 +201,18 @@ public enum LinkControlOpcode
return values()[value];
}
break;
case HARRIS:
switch(value)
{
case 10:
return L3HARRIS_UNKNOWN_0A;
case 42:
return L3HARRIS_UNKNOWN_2A;
case 43:
return L3HARRIS_UNKNOWN_2B;
default:
return L3HARRIS_UNKNOWN;
}
case MOTOROLA:
switch(value)
{
@@ -209,11 +231,11 @@ public enum LinkControlOpcode
default:
return MOTOROLA_UNKNOWN;
}
default:
if(0 <= value && value <= 63)
{
return values()[value];
}
}
if(0 <= value && value <= 63)
{
return values()[value];
}
return UNKNOWN;

View File

@@ -20,6 +20,9 @@
package io.github.dsheirer.module.decode.p25.phase1.message.lc;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisUnknownOpcode10;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisUnknownOpcode42;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisUnknownOpcode43;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaPatchGroupAdd;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaPatchGroupDelete;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaPatchGroupVoiceChannelUpdate;
@@ -128,6 +131,15 @@ public class LinkControlWordFactory
case UNIT_TO_UNIT_VOICE_CHANNEL_USER:
return new LCUnitToUnitVoiceChannelUser(binaryMessage);
case L3HARRIS_UNKNOWN_0A:
return new LCHarrisUnknownOpcode10(binaryMessage);
case L3HARRIS_UNKNOWN_2A:
return new LCHarrisUnknownOpcode42(binaryMessage);
case L3HARRIS_UNKNOWN_2B:
return new LCHarrisUnknownOpcode43(binaryMessage);
case L3HARRIS_UNKNOWN:
return new UnknownLinkControlWord(binaryMessage);
case MOTOROLA_PATCH_GROUP_ADD:
return new LCMotorolaPatchGroupAdd(binaryMessage);
case MOTOROLA_PATCH_GROUP_DELETE:

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
import java.util.ArrayList;
import java.util.List;
/**
* L3Harris Unknown Opcode 10 (0x0A)
* <p>
* Observed in a traffic channel carried by a TDU during an SNDCP packet data session. The controller send an 'All
* blocks received' PDU to the radio in the middle of a stream of these TDU/LC messages and the traffic channel ended
* with Call Termination TDULC messages. So, this may be some form of current data session user information update.
*/
public class LCHarrisUnknownOpcode10 extends LinkControlWord
{
private static final int[] UNKNOWN_1 = {16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] TARGET_RADIO = {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_RADIO = {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 RadioIdentifier mTargetRadio;
private List<Identifier> mIdentifiers;
/**
* Constructs a Link Control Word from the binary message sequence.
*
* @param message
*/
public LCHarrisUnknownOpcode10(BinaryMessage message)
{
super(message);
}
public String toString()
{
StringBuilder sb = new StringBuilder();
if(!isValid())
{
sb.append("**CRC-FAILED** ");
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");
}
else
{
sb.append("L3HARRIS UNKNOWN OPCODE 10 ");
sb.append(" FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());
sb.append(" MSG:").append(getMessage().toHexString());
}
return sb.toString();
}
public String getUnknown()
{
return getMessage().getHex(UNKNOWN_1, 2);
}
/**
* Source radio identifier
*/
public RadioIdentifier getSourceRadio()
{
if(mSourceRadio == null)
{
mSourceRadio = APCO25RadioIdentifier.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceRadio;
}
/**
* Target radio identifier
*/
public RadioIdentifier getTargetRadio()
{
if(mTargetRadio == null)
{
mTargetRadio = APCO25RadioIdentifier.createTo(getMessage().getInt(TARGET_RADIO));
}
return mTargetRadio;
}
/**
* List of identifiers contained in this message
*/
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getSourceRadio());
}
return mIdentifiers;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
import java.util.Collections;
import java.util.List;
/**
* L3Harris Unknown Opcode 42 (0x2A)
* <p>
* Observed in a voice traffic channel carried by an LDU1. This was transmitted once followed by an Opcode 43 (0x2B)
* which is also unknown.
*/
public class LCHarrisUnknownOpcode42 extends LinkControlWord
{
/**
* Constructs a Link Control Word from the binary message sequence.
*
* @param message
*/
public LCHarrisUnknownOpcode42(BinaryMessage message)
{
super(message);
}
public String toString()
{
StringBuilder sb = new StringBuilder();
if(!isValid())
{
sb.append("**CRC-FAILED** ");
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");
}
else
{
sb.append("L3HARRIS UNKNOWN OPCODE 42 MSG:").append(getMessage().toHexString());
}
return sb.toString();
}
/**
* List of identifiers contained in this message
*/
@Override
public List<Identifier> getIdentifiers()
{
return Collections.emptyList();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
import java.util.Collections;
import java.util.List;
/**
* L3Harris Unknown Opcode 43 (0x2B)
* <p>
* Observed in a voice traffic channel carried by an LDU1. This was transmitted once following an Opcode 42 (0x2A)
* which is also unknown.
*/
public class LCHarrisUnknownOpcode43 extends LinkControlWord
{
/**
* Constructs a Link Control Word from the binary message sequence.
*
* @param message
*/
public LCHarrisUnknownOpcode43(BinaryMessage message)
{
super(message);
}
public String toString()
{
StringBuilder sb = new StringBuilder();
if(!isValid())
{
sb.append("**CRC-FAILED** ");
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");
}
else
{
sb.append("L3HARRIS UNKNOWN OPCODE 43 MSG:").append(getMessage().toHexString());
}
return sb.toString();
}
/**
* List of identifiers contained in this message
*/
@Override
public List<Identifier> getIdentifiers()
{
return Collections.emptyList();
}
}

View File

@@ -66,7 +66,7 @@ public class LCMotorolaPatchGroupAdd extends MotorolaLinkControlWord
if(mPatchGroup == null)
{
PatchGroup patchGroup = new PatchGroup(APCO25Talkgroup.create(getMessage().getInt(PATCH_GROUP)));
patchGroup.addPatchedGroups(getPatchedGroups());
patchGroup.addPatchedTalkgroups(getPatchedGroups());
mPatchGroup = APCO25PatchGroup.create(patchGroup);
}

View File

@@ -66,7 +66,7 @@ public class LCMotorolaPatchGroupDelete extends MotorolaLinkControlWord
if(mPatchGroup == null)
{
PatchGroup patchGroup = new PatchGroup(APCO25Talkgroup.create(getMessage().getInt(PATCH_GROUP)));
patchGroup.addPatchedGroups(getPatchedGroups());
patchGroup.addPatchedTalkgroups(getPatchedGroups());
mPatchGroup = APCO25PatchGroup.create(patchGroup);
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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
@@ -15,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp;
@@ -29,7 +28,6 @@ import io.github.dsheirer.module.decode.p25.reference.NetworkAddressType;
import io.github.dsheirer.module.decode.p25.reference.PDUPriorityMaximumType;
import io.github.dsheirer.module.decode.p25.reference.ReadyTimer;
import io.github.dsheirer.module.decode.p25.reference.StandbyTimer;
import java.util.ArrayList;
import java.util.List;
@@ -81,7 +79,7 @@ public class ActivateTdsContextAccept extends SNDCPMessage
}
sb.append(" READY TIMER:").append(getReadyTimer().getSeconds()).append("s");
sb.append(" STANDBY TIMER:").append(getStandbyTimer().getSeconds()).append("s");
sb.append(" MTU:").append(getMaximumTransmitUnit().getByteCount()).append("B");
sb.append(" MTU:").append(getMaximumTransmitUnit().getByteCount());
sb.append(" AUTHORIZED GROUPS:").append(getDataAccessGroups());
return sb.toString();
}

View File

@@ -176,11 +176,11 @@ public enum Opcode
MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN(14, "CCH PLND SHUTDWN", "CONTROL CHANNEL PLANNED SHUTDOWN"),
MOTOROLA_OSP_UNKNOWN(-1, "MOTOROLA OSP UNKNOWN OPCODE", "MOTOROLA OSP UNKNOWN OPCODE"),
//Vendor: motorola, Inbound Service Packet (ISP)
//Vendor: L3Harris, Inbound Service Packet (ISP)
HARRIS_ISP_UNKNOWN(-1, "HARRIS ISP UNKNOWN OPCODE", "HARRIS ISP UNKNOWN OPCODE"),
//Vendor: harris, Outbound Service Packet (OSP)
HARRIS_OSP_TDMA_SYNC(48, "HARRIS TDMA SYNC", "HARRIS TDMA SYNC BROADCAST"),
//Vendor: L3Harris, Outbound Service Packet (OSP)
HARRIS_OSP_GRG_EXENC_CMD(48, "HARRIS REGROUP", "HARRIS GROUP REGROUP COMMAND WITH EXPLICIT ENCRYPTION"),
HARRIS_OSP_UNKNOWN(-1, "HARRIS OSP UNKNOWN OPCODE", "HARRIS OSP UNKNOWN OPCODE"),
//Vendor: unknown, Inbound Service Packet (ISP)
@@ -294,7 +294,7 @@ public enum Opcode
/**
* Harris opcodes
*/
public static final EnumSet<Opcode> HARRIS = EnumSet.of(HARRIS_ISP_UNKNOWN, HARRIS_OSP_TDMA_SYNC,
public static final EnumSet<Opcode> HARRIS = EnumSet.of(HARRIS_ISP_UNKNOWN, HARRIS_OSP_GRG_EXENC_CMD,
HARRIS_OSP_UNKNOWN);
/**
@@ -425,7 +425,7 @@ public enum Opcode
switch(value)
{
case 48:
return HARRIS_OSP_TDMA_SYNC;
return HARRIS_OSP_GRG_EXENC_CMD;
default:
return HARRIS_OSP_UNKNOWN;
}

View File

@@ -27,7 +27,7 @@ import io.github.dsheirer.edac.trellis.ViterbiDecoder_1_2_P25;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DataUnitID;
import io.github.dsheirer.module.decode.p25.phase1.P25P1Interleave;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.isp.UnknownHarrisISPMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp.HarrisTDMASyncBroadcast;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp.HarrisGroupRegroupExplicitEncryptionCommand;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp.UnknownHarrisOSPMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.motorola.isp.UnknownMotorolaISPMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.motorola.osp.ChannelLoading;
@@ -298,8 +298,8 @@ public class TSBKMessageFactory
case HARRIS_ISP_UNKNOWN:
return new UnknownHarrisISPMessage(dataUnitID, message, nac, timestamp);
case HARRIS_OSP_TDMA_SYNC:
return new HarrisTDMASyncBroadcast(dataUnitID, message, nac, timestamp);
case HARRIS_OSP_GRG_EXENC_CMD:
return new HarrisGroupRegroupExplicitEncryptionCommand(dataUnitID, message, nac, timestamp);
case HARRIS_OSP_UNKNOWN:
return new UnknownHarrisOSPMessage(dataUnitID, message, nac, timestamp);

View File

@@ -0,0 +1,200 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.patch.PatchGroup;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup;
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DataUnitID;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.OSPMessage;
import io.github.dsheirer.module.decode.p25.reference.RegroupOptions;
import java.util.ArrayList;
import java.util.List;
/**
* Harris Group Regroup Action and Explicit Encryption Command.
* <p>
* This OSP is used to create, remove or update a Supergroup address associated with one member WGID or WUID with
* explicit encryption parameters.
*
* See: https://forums.radioreference.com/threads/duke-energy-p25-system.411183/page-28#post-3908078
*/
public class HarrisGroupRegroupExplicitEncryptionCommand extends OSPMessage
{
private static final int[] REGROUP_OPTIONS = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] SUPERGROUP_ADDRESS = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
private static final int[] ENCRYPTION_KEY_ID = new int[]{40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] TARGET_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 TalkgroupIdentifier mSuperGroupIdentifier;
private APCO25PatchGroup mPatchGroup;
private Identifier mPatchedIdentifier;
private List<Identifier> mIdentifiers;
private RegroupOptions mRegroupOptions;
/**
* Constructs a TSBK from the binary message sequence.
*/
public HarrisGroupRegroupExplicitEncryptionCommand(P25P1DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp)
{
super(dataUnitId, message, nac, timestamp);
}
/**
* Options for this regrouping that indicate the regrouping actions and targets.
* @return regroup options
*/
public RegroupOptions getRegroupOptions()
{
if(mRegroupOptions == null)
{
mRegroupOptions = new RegroupOptions(getMessage().getInt(REGROUP_OPTIONS));
}
return mRegroupOptions;
}
/**
* Encryption key ID to use for the patch group. Note: this only applies for patched talkgroups and the value is
* zero if the target address is a radio identifier.
* @return encryption key identifier
*/
public int getEncryptionKeyId()
{
return getMessage().getInt(ENCRYPTION_KEY_ID);
}
/**
* Indicates if there is a specified encryption key to use for the patch or simulselect communications.
* @return true if key is specified.
*/
public boolean hasEncryptionKeyId()
{
return getEncryptionKeyId() > 0;
}
/**
* APCO25 Patch Group or Simul-Select (e.g. Harris) loaded with the target address specified in this message.
* @return patch group
*/
public APCO25PatchGroup getPatchGroup()
{
if(mPatchGroup == null)
{
PatchGroup patchGroup = new PatchGroup(getSuperGroup(), getRegroupOptions().getSupergroupSequenceNumber());
if(getTargetAddress() instanceof APCO25Talkgroup talkgroup)
{
patchGroup.addPatchedTalkgroup(talkgroup);
}
else if(getTargetAddress() instanceof APCO25RadioIdentifier radio)
{
patchGroup.addPatchedRadio(radio);
}
mPatchGroup = APCO25PatchGroup.create(patchGroup);
}
return mPatchGroup;
}
/**
* Target address for the regrouping action.
* @return target talkgroup or radio address.
*/
public Identifier getTargetAddress()
{
if(mPatchedIdentifier == null)
{
if(getRegroupOptions().isTalkgroupAddress())
{
mPatchedIdentifier = APCO25Talkgroup.create(getMessage().getInt(TARGET_ADDRESS));
}
else
{
mPatchedIdentifier = APCO25RadioIdentifier.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}
return mPatchedIdentifier;
}
/**
* Talkgroup identifier for the super group aka patch group
*
* @return patch group identifier
*/
public TalkgroupIdentifier getSuperGroup()
{
if(mSuperGroupIdentifier == null)
{
mSuperGroupIdentifier = APCO25Talkgroup.create(getMessage().getInt(SUPERGROUP_ADDRESS));
}
return mSuperGroupIdentifier;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(getMessageStub());
if(getRegroupOptions().isActivate())
{
sb.append(" ACTIVATE");
sb.append(getRegroupOptions().isPatch() ? " TWO-WAY PATCH" : " ONE-WAY SIMUL-SELECT");
sb.append(" SUPERGROUP:").append(getSuperGroup());
sb.append(" V").append(getRegroupOptions().getSupergroupSequenceNumber());
sb.append(" INCLUDE");
sb.append(getRegroupOptions().isTalkgroupAddress() ? " TALKGROUP:" : " RADIO:");
sb.append(getTargetAddress());
if(hasEncryptionKeyId())
{
sb.append(" USE ENCRYPTION KEY:").append(getEncryptionKeyId());
}
}
else
{
sb.append(" DEACTIVATE SUPERGROUP:").append(getSuperGroup());
sb.append(" V").append(getRegroupOptions().getSupergroupSequenceNumber());
}
return sb.toString();
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getPatchGroup());
}
return mIdentifiers;
}
}

View File

@@ -1,58 +0,0 @@
/*******************************************************************************
* sdr-trunk
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.module.decode.p25.phase1.message.tsbk.harris.osp;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DataUnitID;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.OSPMessage;
import java.util.Collections;
import java.util.List;
/**
* Harris TDMA Sync Broadcast message.
*
* Note: Standard Opcode 48 is a TDMA Sync message, so this likely has something
* to do with TDMA Phase II
*/
public class HarrisTDMASyncBroadcast extends OSPMessage
{
/**
* Constructs a TSBK from the binary message sequence.
*/
public HarrisTDMASyncBroadcast(P25P1DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp)
{
super(dataUnitId, message, nac, timestamp);
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(getMessageStub());
sb.append(" HARRIS TDMA SYNC BROADCAST");
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
@Override
public List<Identifier> getIdentifiers()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -66,7 +66,7 @@ public class PatchGroupAdd extends OSPMessage
if(mPatchGroup == null)
{
PatchGroup patchGroup = new PatchGroup(APCO25Talkgroup.create(getPatchAddress()));
patchGroup.addPatchedGroups(getPatchedTalkgroups());
patchGroup.addPatchedTalkgroups(getPatchedTalkgroups());
mPatchGroup = APCO25PatchGroup.create(patchGroup);
}

View File

@@ -66,7 +66,7 @@ public class PatchGroupDelete extends OSPMessage
if(mPatchGroup == null)
{
PatchGroup patchGroup = new PatchGroup(APCO25Talkgroup.create(getPatchAddress()));
patchGroup.addPatchedGroups(getPatchedTalkgroups());
patchGroup.addPatchedTalkgroups(getPatchedTalkgroups());
mPatchGroup = APCO25PatchGroup.create(patchGroup);
}

View File

@@ -87,6 +87,7 @@ import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToU
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToUnitVoiceChannelGrantUpdateExtended;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToUnitVoiceChannelUserAbbreviated;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToUnitVoiceChannelUserExtended;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris.L3HarrisRegroupCommand;
import io.github.dsheirer.module.decode.p25.phase2.timeslot.AbstractVoiceTimeslot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -391,6 +392,19 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
case PHASE1_109_UNIT_REGISTRATION_COMMAND_ABBREVIATED:
broadcast(message, mac, getCurrentChannel(), DecodeEventType.COMMAND, "UNIT REGISTRATION");
break;
case PHASE1_176_L3HARRIS_GROUP_REGROUP:
if(mac instanceof L3HarrisRegroupCommand regroup)
{
if(regroup.getRegroupOptions().isActivate())
{
mPatchGroupManager.addPatchGroup(regroup.getPatchGroup());
}
else
{
mPatchGroupManager.removePatchGroup(regroup.getPatchGroup());
}
}
break;
case PHASE1_197_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED:
if(mac instanceof UnitToUnitAnswerRequestExtended uuare)
{

View File

@@ -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 <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase2.message.mac;
@@ -87,11 +84,11 @@ import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToU
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToUnitVoiceChannelUserExtended;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnknownStructure;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnknownVendorMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris.L3HarrisRegroupCommand;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Message factory for creating Encoded MAC Information (EMI) message parser instances.
@@ -293,6 +290,8 @@ public class MacMessageFactory
return new AdjacentStatusBroadcastAbbreviated(message, offset);
case PHASE1_125_IDENTIFIER_UPDATE:
return new FrequencyBandUpdate(message, offset);
case PHASE1_176_L3HARRIS_GROUP_REGROUP:
return new L3HarrisRegroupCommand(message, offset);
case PHASE1_192_GROUP_VOICE_CHANNEL_GRANT_EXTENDED:
return new GroupVoiceChannelGrantExtended(message, offset);
case PHASE1_195_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:

View File

@@ -76,6 +76,7 @@ public enum MacOpcode
PHASE1_123_NETWORK_STATUS_BROADCAST_ABBREVIATED(123, "NETWORK STATUS BROADCAST ABBREVIATED", 11),
PHASE1_124_ADJACENT_STATUS_BROADCAST_ABBREVIATED(124, "ADJACENT STATUS BROADCAST ABBREVIATED", 9),
PHASE1_125_IDENTIFIER_UPDATE(125, "IDENTIFIER UPDATE", 9),
PHASE1_176_L3HARRIS_GROUP_REGROUP(176, "REGROUP COMMAND", 17),
PHASE1_PARTITION_1_UNKNOWN_OPCODE(-1, "UNKNOWN PHASE 1 OPCODE", -1),
VENDOR_PARTITION_2_UNKNOWN_OPCODE(-1, "UNKNOWN VENDOR OPCODE", -1),
@@ -138,7 +139,7 @@ public enum MacOpcode
TDMA_1_GROUP_VOICE_CHANNEL_USER_ABBREVIATED, TDMA_2_UNIT_TO_UNIT_VOICE_CHANNEL_USER,
TDMA_3_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER, TDMA_5_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE,
TDMA_33_GROUP_VOICE_CHANNEL_USER_EXTENDED, TDMA_34_UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED,
TDMA_37_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_EXPLICIT);
TDMA_37_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_EXPLICIT, PHASE1_176_L3HARRIS_GROUP_REGROUP);
/**
* Data channel grants

View File

@@ -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 <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure;
@@ -26,7 +23,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacStructure;
import java.util.Collections;
import java.util.List;
@@ -72,7 +68,7 @@ public class FrequencyBandUpdate extends MacStructure implements IFrequencyBand
@Override
public int getIdentifier()
{
return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER);
return getMessage().getInt(FREQUENCY_BAND_IDENTIFIER, getOffset());
}
@Override

View File

@@ -0,0 +1,272 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.patch.PatchGroup;
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup;
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacStructure;
import io.github.dsheirer.module.decode.p25.reference.Encryption;
import io.github.dsheirer.module.decode.p25.reference.RegroupOptions;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* L3Harris Group Regroup Explicit Encryption Command. Provides super group activate/deactivate for dynamic regrouping
* of talkgroups or groups of individual radios and includes optional encryption algorithm and key parameters when the
* regrouping targets talkgroups (not radios).
*
* See: https://forums.radioreference.com/threads/duke-energy-p25-system.411183/page-28#post-3908078
*/
public class L3HarrisRegroupCommand extends MacStructure
{
private static final int[] VENDOR = {8, 9, 10, 11, 12, 13, 14, 15};
private static final int[] LENGTH = {18, 19, 20, 21, 22, 23};
private static final int[] REGROUP_OPTIONS = {24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] SUPERGROUP = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] KEY_ID = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
//Variation 1: Radio identifiers only.
private static final int[] RADIO_1 = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 85, 86, 87};
private static final int[] RADIO_2 = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
106, 107, 108, 109, 110, 111};
private static final int[] RADIO_3 = {112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
127, 128, 129, 130, 131, 132, 133, 134, 135};
//Variation 2: Talkgroups and Algorithm ID
private static final int[] ALGORITHM_ID = {64, 65, 66, 67, 68, 69, 70, 71};
private static final int[] TALKGROUP_1 = {72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87};
private static final int[] TALKGROUP_2 = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103};
private static final int[] TALKGROUP_3 = {104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119};
private static final int[] TALKGROUP_4 = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135};
private RegroupOptions mRegroupOptions;
private PatchGroupIdentifier mPatchGroupIdentifier;
private List<Identifier> mIdentifiers;
/**
* Constructs the message
*
* @param message containing the message bits
* @param offset into the message for this structure
*/
public L3HarrisRegroupCommand(CorrectedBinaryMessage message, int offset)
{
super(message, offset);
}
/**
* Textual representation of this message
*/
public String toString()
{
StringBuilder sb = new StringBuilder();
if(getVendor() == Vendor.HARRIS)
{
sb.append("L3HARRIS ");
}
else
{
sb.append("WARNING: UNKNOWN VENDOR:").append(getVendor());
}
if(getRegroupOptions().isActivate())
{
sb.append(" ACTIVATE");
sb.append(getRegroupOptions().isPatch() ? " TWO-WAY PATCH" : " ONE-WAY SIMUL-SELECT");
sb.append(" SUPERGROUP:").append(getPatchGroup().getValue().getPatchGroup().getValue());
sb.append(" V").append(getRegroupOptions().getSupergroupSequenceNumber());
sb.append(" V").append(getRegroupOptions().getSupergroupSequenceNumber());
if(getRegroupOptions().isActivate())
{
if(getRegroupOptions().isTalkgroupAddress())
{
sb.append(" INCLUDE TALKGROUPS:");
sb.append(getPatchGroup().getValue().getPatchedTalkgroupIdentifiers().stream()
.map(tg -> tg.getValue().toString()).collect(Collectors.joining(",")));
Encryption encryption = getEncryptionAlgorithm();
if(encryption != Encryption.UNENCRYPTED)
{
sb.append(" USE ENCRYPTION:").append(encryption.name());
sb.append(" KEY:").append(getKeyId());
}
}
else
{
sb.append(" INCLUDE RADIOS:");
sb.append(getPatchGroup().getValue().getPatchedRadioIdentifiers().stream()
.map(radio -> radio.getValue().toString()).collect(Collectors.joining(",")));
}
}
}
else
{
sb.append(" DEACTIVATE SUPERGROUP:").append(getPatchGroup().getValue().getPatchGroup().getValue());
}
sb.append(" MSG LENGTH:").append(getLength());
return sb.toString();
}
/**
* Vendor ID. This should be L3Harris unless another vendor is also using this Opcode.
*/
public Vendor getVendor()
{
return Vendor.fromValue(getMessage().getInt(VENDOR, getOffset()));
}
/**
* Message length. Note: I'm unsure if this is the count of identifiers or if this is the quantity of bytes.
* @return length.
*/
public int getLength()
{
return getMessage().getInt(LENGTH, getOffset());
}
/**
* Regrouping options for this patch group (ie activate, deactivate, patch group version, etc.
* @return patch group options.
*/
public RegroupOptions getRegroupOptions()
{
if(mRegroupOptions == null)
{
mRegroupOptions = new RegroupOptions(getMessage().getInt(REGROUP_OPTIONS, getOffset()));
}
return mRegroupOptions;
}
/**
* Encryption key ID. Note: only available when the regroup options indicates this is a talkgroup supergroup.
* @return key ID.
*/
public int getKeyId()
{
return getMessage().getInt(KEY_ID, getOffset());
}
/**
* Encryption algorithm for talkgroup super-group.
*/
public Encryption getEncryptionAlgorithm()
{
return Encryption.fromValue(getMessage().getInt(ALGORITHM_ID, getOffset()));
}
/**
* Supergroup/Patch group referenced by this message including any patched talkgroups or individual radio identifiers.
* @return patch group.
*/
public PatchGroupIdentifier getPatchGroup()
{
if(mPatchGroupIdentifier == null)
{
TalkgroupIdentifier patchGroupId = APCO25Talkgroup.create(getMessage().getInt(SUPERGROUP, getOffset()));
PatchGroup patchGroup = new PatchGroup(patchGroupId, getRegroupOptions().getSupergroupSequenceNumber());
if(getRegroupOptions().isTalkgroupAddress())
{
int talkgroup1 = getMessage().getInt(TALKGROUP_1, getOffset());
if(talkgroup1 > 0)
{
patchGroup.addPatchedTalkgroup(APCO25Talkgroup.create(talkgroup1));
int talkgroup2 = getMessage().getInt(TALKGROUP_2, getOffset());
if(talkgroup2 > 0)
{
patchGroup.addPatchedTalkgroup(APCO25Talkgroup.create(talkgroup2));
int talkgroup3 = getMessage().getInt(TALKGROUP_3, getOffset());
if(talkgroup3 > 0)
{
patchGroup.addPatchedTalkgroup(APCO25Talkgroup.create(talkgroup3));
int talkgroup4 = getMessage().getInt(TALKGROUP_4, getOffset());
if(talkgroup4 > 0)
{
patchGroup.addPatchedTalkgroup(APCO25Talkgroup.create(talkgroup4));
}
}
}
}
}
else
{
int radio1 = getMessage().getInt(RADIO_1, getOffset());
if(radio1 > 0)
{
patchGroup.addPatchedRadio(APCO25RadioIdentifier.createTo(radio1));
int radio2 = getMessage().getInt(RADIO_2, getOffset());
if(radio2 > 0)
{
patchGroup.addPatchedRadio(APCO25RadioIdentifier.createTo(radio2));
int radio3 = getMessage().getInt(RADIO_3, getOffset());
if(radio3 > 0)
{
patchGroup.addPatchedRadio(APCO25RadioIdentifier.createTo(radio3));
}
}
}
}
mPatchGroupIdentifier = APCO25PatchGroup.create(patchGroup);
}
return mPatchGroupIdentifier;
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getPatchGroup());
}
return mIdentifiers;
}
}

View File

@@ -0,0 +1,81 @@
package io.github.dsheirer.module.decode.p25.reference;
/**
* Harris group regroup options
*/
public class RegroupOptions
{
private static final int MASK_REGROUP_TYPE = 0x80;
private static final int MASK_GROUP_INDIVIDUAL = 0x40;
private static final int MASK_ACTIVATE = 0x20;
private static final int MASK_SUPERGROUP_SEQUENCE_NUMBER = 0x1F;
private int mValue;
/**
* Constructs an instance
* @param value
*/
public RegroupOptions(int value)
{
mValue = value;
}
/**
* Type of regrouping
* @return regroup type
*/
public RegroupType getRegroupType()
{
return (mValue & MASK_REGROUP_TYPE) == MASK_REGROUP_TYPE ?
RegroupType.SIMULSELECT_ONE_WAY_REGROUP : RegroupType.PATCH_TWO_WAY_REGROUP;
}
/**
* Indicates if this is a two-way patch (true) or one-way simul-select (false) regrouping
* @return patch (true) or simulselect (false)
*/
public boolean isPatch()
{
return getRegroupType() == RegroupType.PATCH_TWO_WAY_REGROUP;
}
/**
* Indicates if the regroup target address is a group address (true) or individual address (false).
* @return true for group or false for individual address.
*/
public boolean isTalkgroupAddress()
{
return (mValue & MASK_GROUP_INDIVIDUAL) == MASK_GROUP_INDIVIDUAL;
}
/**
* Indicates if this is an activation (true) or a deactivation (false) of the supergroup.
* @return activation (true) or deactivation (false)
*/
public boolean isActivate()
{
return (mValue & MASK_ACTIVATE) == MASK_ACTIVATE;
}
/**
* Unique identifier for the regrouping
* @return unique id SSN
*/
public int getSupergroupSequenceNumber()
{
return (mValue & MASK_SUPERGROUP_SEQUENCE_NUMBER);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(isActivate() ? "ACTIVATE" : "DEACTIVATE");
sb.append(isTalkgroupAddress() ? " TALKGROUP" : " INDIVIDUAL");
sb.append(isPatch() ? " PATCH" : " SIMUL-SELECT");
return sb.toString();
}
public enum RegroupType {PATCH_TWO_WAY_REGROUP, SIMULSELECT_ONE_WAY_REGROUP};
}