mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
#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:
@@ -388,6 +388,8 @@ public class AliasList
|
||||
}
|
||||
break;
|
||||
case PATCH_GROUP:
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
|
||||
PatchGroupIdentifier patchGroupIdentifier = (PatchGroupIdentifier)identifier;
|
||||
PatchGroup patchGroup = patchGroupIdentifier.getValue();
|
||||
|
||||
@@ -395,8 +397,6 @@ public class AliasList
|
||||
|
||||
if(patchGroupAliasList != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
|
||||
Alias alias = patchGroupAliasList.getAlias(patchGroup.getPatchGroup());
|
||||
|
||||
if(alias != null)
|
||||
@@ -404,19 +404,36 @@ public class AliasList
|
||||
aliases.add(alias);
|
||||
}
|
||||
|
||||
for(TalkgroupIdentifier patchedGroup: patchGroup.getPatchedGroupIdentifiers())
|
||||
for(TalkgroupIdentifier patchedTalkgroup: patchGroup.getPatchedTalkgroupIdentifiers())
|
||||
{
|
||||
Alias patchedAlias = patchGroupAliasList.getAlias(patchedGroup);
|
||||
Alias patchedTalkgroupAlias = patchGroupAliasList.getAlias(patchedTalkgroup);
|
||||
|
||||
if(patchedAlias != null && !aliases.contains(patchedAlias))
|
||||
if(patchedTalkgroupAlias != null && !aliases.contains(patchedTalkgroupAlias))
|
||||
{
|
||||
aliases.add(patchedAlias);
|
||||
aliases.add(patchedTalkgroupAlias);
|
||||
}
|
||||
}
|
||||
|
||||
return aliases;
|
||||
}
|
||||
break;
|
||||
|
||||
if(patchGroup.hasPatchedRadios())
|
||||
{
|
||||
RadioAliasList radioAliasList = mRadioProtocolMap.get(patchGroupIdentifier.getProtocol());
|
||||
|
||||
if(radioAliasList != null)
|
||||
{
|
||||
for(RadioIdentifier patchedRadio: patchGroup.getPatchedRadioIdentifiers())
|
||||
{
|
||||
Alias patchedRadioAlias = radioAliasList.getAlias(patchedRadio);
|
||||
|
||||
if(patchedRadioAlias != null && !aliases.contains(patchedRadioAlias))
|
||||
{
|
||||
aliases.add(patchedRadioAlias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aliases;
|
||||
case RADIO:
|
||||
RadioIdentifier radio = (RadioIdentifier)identifier;
|
||||
|
||||
@@ -483,9 +500,7 @@ public class AliasList
|
||||
{
|
||||
if(alias != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
return aliases;
|
||||
return Collections.singletonList(alias);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -38,9 +38,6 @@ import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
@@ -48,12 +45,13 @@ import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Audio broadcaster to push completed audio recordings to the Broadcastify call push API.
|
||||
@@ -475,7 +473,11 @@ public class BroadcastifyCallBroadcaster extends AbstractAudioBroadcaster<Broadc
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(patchGroup.getPatchGroup().getValue().toString());
|
||||
for(TalkgroupIdentifier patched: patchGroup.getPatchedGroupIdentifiers())
|
||||
for(TalkgroupIdentifier patched: patchGroup.getPatchedTalkgroupIdentifiers())
|
||||
{
|
||||
sb.append(",").append(patched.getValue());
|
||||
}
|
||||
for(RadioIdentifier patched: patchGroup.getPatchedRadioIdentifiers())
|
||||
{
|
||||
sb.append(",").append(patched.getValue());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,6 @@
|
||||
package io.github.dsheirer.audio.broadcast.rdioscanner;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import com.google.common.base.Joiner;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
@@ -34,7 +33,6 @@ import io.github.dsheirer.gui.playlist.radioreference.RadioReferenceDecoder;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierClass;
|
||||
import io.github.dsheirer.identifier.MutableIdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.configuration.ConfigurationLongIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroup;
|
||||
@@ -42,24 +40,12 @@ import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
@@ -67,6 +53,8 @@ import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
@@ -525,7 +513,12 @@ public class RdioScannerBroadcaster extends AbstractAudioBroadcaster<RdioScanner
|
||||
|
||||
sb.append(patchGroup.getPatchGroup().getValue().toString());
|
||||
|
||||
for(TalkgroupIdentifier patched: patchGroup.getPatchedGroupIdentifiers())
|
||||
for(TalkgroupIdentifier patched: patchGroup.getPatchedTalkgroupIdentifiers())
|
||||
{
|
||||
sb.append(",").append(patched.getValue());
|
||||
}
|
||||
|
||||
for(RadioIdentifier patched: patchGroup.getPatchedRadioIdentifiers())
|
||||
{
|
||||
sb.append(",").append(patched.getValue());
|
||||
}
|
||||
|
||||
@@ -1,69 +1,161 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.identifier.patch;
|
||||
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Temporary grouping or regrouping of talkgroups or individual radios.
|
||||
*/
|
||||
public class PatchGroup
|
||||
{
|
||||
// private final static Logger mLog = LoggerFactory.getLogger(PatchGroup.class);
|
||||
|
||||
private TalkgroupIdentifier mPatchGroupIdentifier;
|
||||
private List<TalkgroupIdentifier> mPatchedGroupIdentifiers = new ArrayList<>();
|
||||
private List<TalkgroupIdentifier> mPatchedTalkgroupIdentifiers = new ArrayList<>();
|
||||
private List<RadioIdentifier> mPatchedRadioIdentifiers = new ArrayList<>();
|
||||
private int mVersion;
|
||||
|
||||
public PatchGroup(TalkgroupIdentifier patchGroupIdentifier)
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param patchGroupIdentifier for the patch group
|
||||
* @param version for this patch group identifier. Note: if the patch group identifier is frequently reused, a
|
||||
* new version number will indicate a new instance of the patch group.
|
||||
*/
|
||||
public PatchGroup(TalkgroupIdentifier patchGroupIdentifier, int version)
|
||||
{
|
||||
mPatchGroupIdentifier = patchGroupIdentifier;
|
||||
mVersion = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param patchGroupIdentifier that is the overall identifier for this temporary grouping.
|
||||
*/
|
||||
public PatchGroup(TalkgroupIdentifier patchGroupIdentifier)
|
||||
{
|
||||
this(patchGroupIdentifier, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version number for this patch group used to differentiate frequently reused patch group identifiers.
|
||||
* @return version number (aka super group sequence number).
|
||||
*/
|
||||
public int getVersion()
|
||||
{
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch group identifier.
|
||||
* @return identifier
|
||||
*/
|
||||
public TalkgroupIdentifier getPatchGroup()
|
||||
{
|
||||
return mPatchGroupIdentifier;
|
||||
}
|
||||
|
||||
public void addPatchedGroup(TalkgroupIdentifier patchedGroupIdentifier)
|
||||
/**
|
||||
* Indicates if this patch group contains any patched talkgroups.
|
||||
* @return true if there are patched talkgroups.
|
||||
*/
|
||||
public boolean hasPatchedTalkgroups()
|
||||
{
|
||||
if(!mPatchedGroupIdentifiers.contains(patchedGroupIdentifier))
|
||||
return !getPatchedTalkgroupIdentifiers().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this patch group contains any patched radio identifiers.
|
||||
* @return true if there are patched radios.
|
||||
*/
|
||||
public boolean hasPatchedRadios()
|
||||
{
|
||||
return !getPatchedRadioIdentifiers().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the talkgroup identifier to this patched group.
|
||||
* @param patchedGroupIdentifier to add
|
||||
*/
|
||||
public void addPatchedTalkgroup(TalkgroupIdentifier patchedGroupIdentifier)
|
||||
{
|
||||
if(!mPatchedTalkgroupIdentifiers.contains(patchedGroupIdentifier))
|
||||
{
|
||||
mPatchedGroupIdentifiers.add(patchedGroupIdentifier);
|
||||
mPatchedTalkgroupIdentifiers.add(patchedGroupIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPatchedGroups(List<TalkgroupIdentifier> patchedGroupIdentifiers)
|
||||
/**
|
||||
* Adds a list of talkgroup identifiers to this patched group.
|
||||
* @param patchedGroupIdentifiers to add
|
||||
*/
|
||||
public void addPatchedTalkgroups(List<TalkgroupIdentifier> patchedGroupIdentifiers)
|
||||
{
|
||||
for(TalkgroupIdentifier identifier : patchedGroupIdentifiers)
|
||||
{
|
||||
addPatchedGroup(identifier);
|
||||
addPatchedTalkgroup(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
public List<TalkgroupIdentifier> getPatchedGroupIdentifiers()
|
||||
/**
|
||||
* List of patched group identifiers that are part of this patch group.
|
||||
* @return patched talkgroups.
|
||||
*/
|
||||
public List<TalkgroupIdentifier> getPatchedTalkgroupIdentifiers()
|
||||
{
|
||||
return mPatchedGroupIdentifiers;
|
||||
return mPatchedTalkgroupIdentifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the radio identifier to this patched group.
|
||||
* @param patchedRadioIdentifier to add
|
||||
*/
|
||||
public void addPatchedRadio(RadioIdentifier patchedRadioIdentifier)
|
||||
{
|
||||
if(!mPatchedRadioIdentifiers.contains(patchedRadioIdentifier))
|
||||
{
|
||||
mPatchedRadioIdentifiers.add(patchedRadioIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of radio identifiers to this patched group.
|
||||
* @param patchedRadioIdentifiers to add
|
||||
*/
|
||||
public void addPatchedRadios(List<RadioIdentifier> patchedRadioIdentifiers)
|
||||
{
|
||||
for(RadioIdentifier identifier : patchedRadioIdentifiers)
|
||||
{
|
||||
addPatchedRadio(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of patched radio identifiers that are part of this patch group.
|
||||
* @return patched radios.
|
||||
*/
|
||||
public List<RadioIdentifier> getPatchedRadioIdentifiers()
|
||||
{
|
||||
return mPatchedRadioIdentifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,9 +164,14 @@ public class PatchGroup
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("P:").append(getPatchGroup());
|
||||
|
||||
if(!getPatchedGroupIdentifiers().isEmpty())
|
||||
if(hasPatchedTalkgroups())
|
||||
{
|
||||
sb.append(" ").append(getPatchedGroupIdentifiers());
|
||||
sb.append(" TG").append(getPatchedTalkgroupIdentifiers());
|
||||
}
|
||||
|
||||
if(hasPatchedRadios())
|
||||
{
|
||||
sb.append(" RA").append(getPatchedRadioIdentifiers());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,27 +22,23 @@ package io.github.dsheirer.identifier.patch;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierClass;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manager for (temporary) patch groups and related identifiers.
|
||||
* Manager for (temporary) patch groups aka super groups. This manager monitors patch group additions and deletions and
|
||||
* maintains a map of the current state of each patch group active in the system.
|
||||
*
|
||||
* Patch groups are primarily used on Motorola P25 systems. This manager monitors patch group additions and deletions
|
||||
* and maintains a map of the current patch groups.
|
||||
*
|
||||
* Traffic channel calls will sometimes reference a patch group by simply using the patch group identifier. This
|
||||
* manager will replace that reference with the full patch group so that the call event has the full patch group
|
||||
* including all of the patched groups, which may not have been included in the patch group reference on the
|
||||
* traffic channel.
|
||||
* Traffic channel calls will reference a patch group by simply using the patch group identifier. This
|
||||
* manager will replace that reference with the current state of the full patch group so that the call event has the
|
||||
* full patch group including all patched talkgroups or individual radios, which may not have been included in the
|
||||
* patch group reference on the control or traffic channel.
|
||||
*/
|
||||
public class PatchGroupManager
|
||||
{
|
||||
// private final static Logger mLog = LoggerFactory.getLogger(PatchGroupManager.class);
|
||||
private Map<Integer,Long> mPatchGroupLastUpdatedMap = new HashMap<>();
|
||||
private Map<Integer,PatchGroupIdentifier> mPatchGroupMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
@@ -58,13 +54,13 @@ public class PatchGroupManager
|
||||
public void clear()
|
||||
{
|
||||
mPatchGroupMap.clear();
|
||||
mPatchGroupLastUpdatedMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the patch group identifier to the set of managed patch groups. If the patch group is already being
|
||||
* managed, the new identifier is checked for additional patched talkgroups and they are added to the existing
|
||||
* managed patch group.
|
||||
* managed, the new patch group argument is checked for additional patched talkgroups and they are added to the
|
||||
* existing managed patch group. Where the patch group version (super group sequence number) is different, the
|
||||
* existing patch group is discarded and replaced with the updated version of the patch group.
|
||||
*
|
||||
* @param patchGroupIdentifier to add or update
|
||||
*/
|
||||
@@ -76,8 +72,19 @@ public class PatchGroupManager
|
||||
{
|
||||
if(mPatchGroupMap.containsKey(update.getPatchGroup().getValue()))
|
||||
{
|
||||
PatchGroup current = mPatchGroupMap.get(update.getPatchGroup().getValue()).getValue();
|
||||
current.addPatchedGroups(update.getPatchedGroupIdentifiers());
|
||||
PatchGroup existingPatchGroup = mPatchGroupMap.get(update.getPatchGroup().getValue()).getValue();
|
||||
|
||||
//If the patch group version number is the same, this is an update
|
||||
if(existingPatchGroup.getVersion() == update.getVersion())
|
||||
{
|
||||
existingPatchGroup.addPatchedTalkgroups(update.getPatchedTalkgroupIdentifiers());
|
||||
existingPatchGroup.addPatchedRadios(update.getPatchedRadioIdentifiers());
|
||||
}
|
||||
//Otherwise, this is a replace operation.
|
||||
else
|
||||
{
|
||||
mPatchGroupMap.put(update.getPatchGroup().getValue(), patchGroupIdentifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -109,7 +116,6 @@ public class PatchGroupManager
|
||||
{
|
||||
int id = patchGroupIdentifier.getValue().getPatchGroup().getValue();
|
||||
mPatchGroupMap.remove(id);
|
||||
mPatchGroupLastUpdatedMap.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,23 +159,28 @@ public class PatchGroupManager
|
||||
*/
|
||||
public Identifier update(Identifier identifier)
|
||||
{
|
||||
if(identifier == null)
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
|
||||
if(identifier.getIdentifierClass() == IdentifierClass.USER && identifier.getRole() == Role.TO)
|
||||
if(identifier != null && identifier.getIdentifierClass() == IdentifierClass.USER && identifier.getRole() == Role.TO)
|
||||
{
|
||||
switch(identifier.getForm())
|
||||
{
|
||||
case PATCH_GROUP:
|
||||
if(identifier instanceof PatchGroupIdentifier)
|
||||
case TALKGROUP:
|
||||
if(identifier instanceof TalkgroupIdentifier talkgroupIdentifier)
|
||||
{
|
||||
Identifier mapValue = mPatchGroupMap.get(talkgroupIdentifier.getValue());
|
||||
if (mapValue != null)
|
||||
{
|
||||
return mapValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PATCH_GROUP:
|
||||
if(identifier instanceof PatchGroupIdentifier patchGroupIdentifier)
|
||||
{
|
||||
PatchGroupIdentifier patchGroupIdentifier = (PatchGroupIdentifier)identifier;
|
||||
int patchGroupId = patchGroupIdentifier.getValue().getPatchGroup().getValue();
|
||||
|
||||
Identifier mapValue = mPatchGroupMap.get(patchGroupId);
|
||||
if (mapValue != null) {
|
||||
if (mapValue != null)
|
||||
{
|
||||
return mapValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
}
|
||||
@@ -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.preference.identifier.talkgroup;
|
||||
@@ -83,10 +80,20 @@ public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
|
||||
sb.append("[");
|
||||
|
||||
int counter = 0;
|
||||
for(TalkgroupIdentifier patchedGroup: patchGroup.getPatchedGroupIdentifiers())
|
||||
for(TalkgroupIdentifier patchedGroup: patchGroup.getPatchedTalkgroupIdentifiers())
|
||||
{
|
||||
sb.append(format(patchedGroup, format, fixedWidth));
|
||||
if(counter++ < patchGroup.getPatchedGroupIdentifiers().size() - 1)
|
||||
if(counter++ < patchGroup.getPatchedTalkgroupIdentifiers().size() - 1)
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
counter = 0;
|
||||
for(RadioIdentifier patchedRadio: patchGroup.getPatchedRadioIdentifiers())
|
||||
{
|
||||
sb.append(format(patchedRadio, format, fixedWidth));
|
||||
if(counter++ < patchGroup.getPatchedRadioIdentifiers().size() - 1)
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user