mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
#2004 P25P1 Link Control Extended Source messages now process through decoder state correctly and display in Now Playing. (#2005)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -47,6 +47,19 @@ public class APCO25FullyQualifiedRadioIdentifier extends FullyQualifiedRadioIden
|
||||
return Protocol.APCO25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if(isAliased())
|
||||
{
|
||||
return "ROAM " + super.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "ISSI " + super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fully qualified radio and assigns the FROM role.
|
||||
* @param localAddress radio identifier. This can be the same as the radio ID when the fully qualified radio
|
||||
|
||||
@@ -20,11 +20,7 @@
|
||||
package io.github.dsheirer.module.decode.p25.identifier.talkgroup;
|
||||
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.FullyQualifiedTalkgroupIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25FullyQualifiedRadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
@@ -51,8 +47,14 @@ public class APCO25FullyQualifiedTalkgroupIdentifier extends FullyQualifiedTalkg
|
||||
return Protocol.APCO25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ISSI " + super.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identifier for the fully qualified talkgroup ising the FROM role.
|
||||
* Creates an identifier for the fully qualified talkgroup using the FROM role.
|
||||
* @param groupAddress used on the local system as an alias to the fully qualified talkgroup.
|
||||
* @param wacn for the talkgroup home system.
|
||||
* @param system for the talkgroup home system.
|
||||
|
||||
@@ -61,6 +61,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25P1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HeaderData;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisReturnToControlChannel;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasComplete;
|
||||
@@ -318,6 +319,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(iMessage instanceof IExtendedSourceMessage esm && iMessage instanceof LinkControlWord lcw)
|
||||
{
|
||||
processLC(lcw, esm.getTimestamp(), esm.isTerminator());
|
||||
}
|
||||
else if(iMessage instanceof MotorolaTalkerAliasComplete tac)
|
||||
{
|
||||
mTrafficChannelManager.getTalkerAliasManager().update(tac.getRadio(), tac.getAlias());
|
||||
|
||||
@@ -23,9 +23,10 @@ import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.P25FrequencyBandPreloadDataContent;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBandReceiver;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.ExtendedSourceLinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.HarrisTalkerAliasAssembler;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasBase;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkerAliasAssembler;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
|
||||
@@ -60,7 +61,7 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
/**
|
||||
* Temporary holding of an extended source link control message while it awaits the extension message to arrive.
|
||||
*/
|
||||
private ExtendedSourceLinkControlWord mExtendedSourceLinkControlWord;
|
||||
private IExtendedSourceMessage mExtendedSourceMessage;
|
||||
|
||||
/**
|
||||
* Motorola talker alias assembler for link control header and data blocks.
|
||||
@@ -110,18 +111,26 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
//Reassemble extended source link control messages.
|
||||
if(message instanceof LDU1Message ldu)
|
||||
{
|
||||
reassembleLC(ldu.getLinkControlWord());
|
||||
|
||||
//Send the LCW to the harris talker alias assembler
|
||||
additionalMessageToSend = mHarrisTalkerAliasAssembler.process(ldu.getLinkControlWord(), ldu.getTimestamp());
|
||||
if(ldu.getLinkControlWord() instanceof IExtendedSourceMessage extendedSourceMessage)
|
||||
{
|
||||
additionalMessageToSend = reassembleLC(extendedSourceMessage);
|
||||
}
|
||||
else if(ldu.getLinkControlWord() instanceof LCHarrisTalkerAliasBase harrisTalkerAlias)
|
||||
{
|
||||
//Send the LCW to the harris talker alias assembler
|
||||
additionalMessageToSend = mHarrisTalkerAliasAssembler.process(harrisTalkerAlias, ldu.getTimestamp());
|
||||
}
|
||||
}
|
||||
else if(message instanceof TDULinkControlMessage tdu)
|
||||
{
|
||||
LinkControlWord lcw = tdu.getLinkControlWord();
|
||||
reassembleLC(lcw);
|
||||
|
||||
if(lcw instanceof IExtendedSourceMessage extendedSourceMessage)
|
||||
{
|
||||
additionalMessageToSend = reassembleLC(extendedSourceMessage);
|
||||
}
|
||||
//Motorola carries the talker alias in the TDULC
|
||||
if(mMotorolaTalkerAliasAssembler.add(lcw, message.getTimestamp()))
|
||||
else if(mMotorolaTalkerAliasAssembler.add(lcw, message.getTimestamp()))
|
||||
{
|
||||
additionalMessageToSend = mMotorolaTalkerAliasAssembler.assemble();
|
||||
}
|
||||
@@ -168,7 +177,10 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
|
||||
if(mMessageListener != null)
|
||||
{
|
||||
mMessageListener.receive(message);
|
||||
if(message != null)
|
||||
{
|
||||
mMessageListener.receive(message);
|
||||
}
|
||||
|
||||
if(additionalMessageToSend != null)
|
||||
{
|
||||
@@ -179,26 +191,32 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
|
||||
/**
|
||||
* Processes link control words to reassemble source ID extension messages.
|
||||
* @param linkControlWord to process
|
||||
* @param lcw to process
|
||||
* @return reassembled link control or null if the original lcw is a receiver (which we hold onto and send later)
|
||||
*/
|
||||
private void reassembleLC(LinkControlWord linkControlWord)
|
||||
private IMessage reassembleLC(IExtendedSourceMessage lcw)
|
||||
{
|
||||
if(linkControlWord instanceof ExtendedSourceLinkControlWord eslcw)
|
||||
IMessage toReturn = null;
|
||||
|
||||
if(lcw instanceof IExtendedSourceMessage extendedSourceMessage && extendedSourceMessage.isExtensionRequired())
|
||||
{
|
||||
mExtendedSourceLinkControlWord = eslcw;
|
||||
mExtendedSourceMessage = extendedSourceMessage;
|
||||
}
|
||||
else if(linkControlWord instanceof LCSourceIDExtension sie && mExtendedSourceLinkControlWord != null)
|
||||
else if(lcw instanceof LCSourceIDExtension sie && mExtendedSourceMessage != null)
|
||||
{
|
||||
mExtendedSourceLinkControlWord.setSourceIDExtension(sie);
|
||||
mExtendedSourceLinkControlWord = null;
|
||||
mExtendedSourceMessage.setSourceIDExtension(sie);
|
||||
toReturn = mExtendedSourceMessage;
|
||||
mExtendedSourceMessage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//The source extension message should always immediately follow the message that is being extended, so if
|
||||
//we get a message that is not an extended message or the extension, then we've missed the extension and we
|
||||
//should nullify any extended message that's waiting for the extension.
|
||||
mExtendedSourceLinkControlWord = null;
|
||||
mExtendedSourceMessage = null;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,28 +21,63 @@ package io.github.dsheirer.module.decode.p25.phase1.message.lc;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.FullyQualifiedRadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25FullyQualifiedRadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25P1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Extended link control word. Base class for adding in the extension word for messages that require two link control
|
||||
* fragments to fit the content.
|
||||
*/
|
||||
public abstract class ExtendedSourceLinkControlWord extends LinkControlWord
|
||||
public abstract class ExtendedSourceLinkControlWord extends LinkControlWord implements IExtendedSourceMessage
|
||||
{
|
||||
private static final IntField SOURCE_ADDRESS = IntField.length24(OCTET_6_BIT_48);
|
||||
private LCSourceIDExtension mSourceIDExtension;
|
||||
private FullyQualifiedRadioIdentifier mSourceAddress;
|
||||
private long mTimestamp;
|
||||
private boolean mTerminator;
|
||||
protected List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param timestamp of this message.
|
||||
* @param isTerminator to indicate if message is carried by a TDULC terminator message
|
||||
*/
|
||||
public ExtendedSourceLinkControlWord(CorrectedBinaryMessage message)
|
||||
public ExtendedSourceLinkControlWord(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
mTimestamp = timestamp;
|
||||
mTerminator = isTerminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminator()
|
||||
{
|
||||
return mTerminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return Protocol.APCO25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeslot()
|
||||
{
|
||||
return P25P1Message.TIMESLOT_0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +94,26 @@ public abstract class ExtendedSourceLinkControlWord extends LinkControlWord
|
||||
public void setSourceIDExtension(LCSourceIDExtension extension)
|
||||
{
|
||||
mSourceIDExtension = extension;
|
||||
mSourceAddress = null;
|
||||
mIdentifiers = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this LCW always requires an extended source.
|
||||
*/
|
||||
@Override
|
||||
public boolean isExtensionRequired()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message has the source ID extension message appended.
|
||||
*/
|
||||
@Override
|
||||
public boolean isFullyExtended()
|
||||
{
|
||||
return mSourceIDExtension != null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.lc;
|
||||
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension;
|
||||
|
||||
/**
|
||||
* Interface to identify Link Control messages that can optionally be extended by appending source ID extension message.
|
||||
*/
|
||||
public interface IExtendedSourceMessage extends IMessage
|
||||
{
|
||||
/**
|
||||
* Sets or assigns the optional source ID extension message.
|
||||
* @param sourceIDExtension to append.
|
||||
*/
|
||||
void setSourceIDExtension(LCSourceIDExtension sourceIDExtension);
|
||||
|
||||
/**
|
||||
* Indicates if this message requires an optional source extension message.
|
||||
* @return true if required.
|
||||
*/
|
||||
boolean isExtensionRequired();
|
||||
|
||||
/**
|
||||
* Indicates if this message requires an extended source and if that extended source is appended.
|
||||
* @return true if fully extended.
|
||||
*/
|
||||
boolean isFullyExtended();
|
||||
|
||||
/**
|
||||
* Indicates if this link control message was carried by a TDULC terminator message.
|
||||
* @return true if terminator.
|
||||
*/
|
||||
boolean isTerminator();
|
||||
}
|
||||
@@ -81,8 +81,10 @@ public class LinkControlWordFactory
|
||||
* Creates a link control word from the binary message sequence.
|
||||
*
|
||||
* @param message containing the LCW binary message sequence.
|
||||
* @param timestamp of the message that carries the link control word.
|
||||
* @param isTerminator to indicate if message is carried by a TDULC terminator message
|
||||
*/
|
||||
public static LinkControlWord create(CorrectedBinaryMessage message)
|
||||
public static LinkControlWord create(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
LinkControlOpcode opcode = LinkControlWord.getOpcode(message);
|
||||
switch(opcode)
|
||||
@@ -104,11 +106,11 @@ public class LinkControlWordFactory
|
||||
case EXTENDED_FUNCTION_COMMAND:
|
||||
return new LCExtendedFunctionCommand(message);
|
||||
case EXTENDED_FUNCTION_COMMAND_EXTENDED:
|
||||
return new LCExtendedFunctionCommandExtended(message);
|
||||
return new LCExtendedFunctionCommandExtended(message, timestamp, isTerminator);
|
||||
case GROUP_AFFILIATION_QUERY:
|
||||
return new LCGroupAffiliationQuery(message);
|
||||
case GROUP_VOICE_CHANNEL_USER:
|
||||
return new LCGroupVoiceChannelUser(message);
|
||||
return new LCGroupVoiceChannelUser(message, timestamp, isTerminator);
|
||||
case GROUP_VOICE_CHANNEL_UPDATE:
|
||||
return new LCGroupVoiceChannelUpdate(message);
|
||||
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
|
||||
@@ -116,7 +118,7 @@ public class LinkControlWordFactory
|
||||
case MESSAGE_UPDATE:
|
||||
return new LCMessageUpdate(message);
|
||||
case MESSAGE_UPDATE_EXTENDED:
|
||||
return new LCMessageUpdateExtended(message);
|
||||
return new LCMessageUpdateExtended(message, timestamp, isTerminator);
|
||||
case NETWORK_STATUS_BROADCAST:
|
||||
return new LCNetworkStatusBroadcast(message);
|
||||
case NETWORK_STATUS_BROADCAST_EXPLICIT:
|
||||
@@ -132,13 +134,13 @@ public class LinkControlWordFactory
|
||||
case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT:
|
||||
return new LCSecondaryControlChannelBroadcastExplicit(message);
|
||||
case SOURCE_ID_EXTENSION:
|
||||
return new LCSourceIDExtension(message);
|
||||
return new LCSourceIDExtension(message, timestamp, isTerminator);
|
||||
case STATUS_QUERY:
|
||||
return new LCStatusQuery(message);
|
||||
case STATUS_UPDATE:
|
||||
return new LCStatusUpdate(message);
|
||||
case STATUS_UPDATE_EXTENDED:
|
||||
return new LCStatusUpdateExtended(message);
|
||||
return new LCStatusUpdateExtended(message, timestamp, isTerminator);
|
||||
case SYSTEM_SERVICE_BROADCAST:
|
||||
return new LCSystemServiceBroadcast(message);
|
||||
case TELEPHONE_INTERCONNECT_ANSWER_REQUEST:
|
||||
@@ -154,7 +156,7 @@ public class LinkControlWordFactory
|
||||
case UNIT_TO_UNIT_VOICE_CHANNEL_USER:
|
||||
return new LCUnitToUnitVoiceChannelUser(message);
|
||||
case UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED:
|
||||
return new LCUnitToUnitVoiceChannelUserExtended(message);
|
||||
return new LCUnitToUnitVoiceChannelUserExtended(message, timestamp, isTerminator);
|
||||
|
||||
case L3HARRIS_RETURN_TO_CONTROL_CHANNEL:
|
||||
return new LCHarrisReturnToControlChannel(message);
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris;
|
||||
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
|
||||
/**
|
||||
* Assembles a talker alias from talker alias blocks 1-4
|
||||
*/
|
||||
@@ -55,7 +53,7 @@ public class HarrisTalkerAliasAssembler
|
||||
* @param lcw containing Harris LC talker alias blocks 1-4
|
||||
* @return fully assembled talker alias, if available, or null.
|
||||
*/
|
||||
public LCHarrisTalkerAliasComplete process(LinkControlWord lcw, long timestamp)
|
||||
public LCHarrisTalkerAliasComplete process(LCHarrisTalkerAliasBase lcw, long timestamp)
|
||||
{
|
||||
mTimestamp = timestamp;
|
||||
|
||||
|
||||
@@ -41,16 +41,17 @@ public class LCExtendedFunctionCommandExtended extends ExtendedSourceLinkControl
|
||||
|
||||
private Identifier mTargetAddress;
|
||||
private FullyQualifiedRadioIdentifier mSourceAddress;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param timestamp of the carrier message
|
||||
* @param isTerminator to indicate if message is carried by a TDULC terminator message
|
||||
*/
|
||||
public LCExtendedFunctionCommandExtended(CorrectedBinaryMessage message)
|
||||
public LCExtendedFunctionCommandExtended(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
super(message, timestamp, isTerminator);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
|
||||
@@ -22,31 +22,43 @@ package io.github.dsheirer.module.decode.p25.phase1.message.lc.standard;
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25FullyQualifiedRadioIdentifier;
|
||||
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.message.P25P1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.VoiceLinkControlMessage;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Group voice channel user.
|
||||
*/
|
||||
public class LCGroupVoiceChannelUser extends VoiceLinkControlMessage
|
||||
public class LCGroupVoiceChannelUser extends VoiceLinkControlMessage implements IExtendedSourceMessage
|
||||
{
|
||||
private static final IntField GROUP_ADDRESS = IntField.length16(OCTET_4_BIT_32);
|
||||
private static final int SOURCE_ADDRESS_EXTENSION_FLAG = OCTET_3_BIT_24 + 7;
|
||||
private static final IntField SOURCE_ADDRESS = IntField.length24(OCTET_6_BIT_48);
|
||||
private Identifier mGroupAddress;
|
||||
private Identifier mSourceAddress;
|
||||
private List<Identifier> mIdentifiers;
|
||||
private LCSourceIDExtension mSourceIdExtension;
|
||||
private long mTimestamp;
|
||||
private boolean mTerminator;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param timestamp of the carrier message
|
||||
* @param isTerminator to indicate if the carrier message is a TDULC terminator message
|
||||
*/
|
||||
public LCGroupVoiceChannelUser(CorrectedBinaryMessage message)
|
||||
public LCGroupVoiceChannelUser(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
mTimestamp = timestamp;
|
||||
mTerminator = isTerminator;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
@@ -54,11 +66,71 @@ public class LCGroupVoiceChannelUser extends VoiceLinkControlMessage
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getMessageStub());
|
||||
sb.append(" FM:").append(getSourceAddress());
|
||||
|
||||
if(isExtensionRequired() && !isFullyExtended())
|
||||
{
|
||||
sb.append(" (INCOMPLETE-FULLY QUALIFIED SOURCE REQUIRED)");
|
||||
}
|
||||
|
||||
sb.append(" TO:").append(getGroupAddress());
|
||||
sb.append(" SERVICE OPTIONS:").append(getServiceOptions());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminator()
|
||||
{
|
||||
return mTerminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullyExtended()
|
||||
{
|
||||
return isExtensionRequired() && mSourceIdExtension != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return Protocol.APCO25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeslot()
|
||||
{
|
||||
return P25P1Message.TIMESLOT_0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message requires an optional source ID extension.
|
||||
* @return true if required.
|
||||
*/
|
||||
@Override
|
||||
public boolean isExtensionRequired()
|
||||
{
|
||||
return getMessage().get(SOURCE_ADDRESS_EXTENSION_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the optional source ID extension for this message.
|
||||
* @param sourceIDExtension to add
|
||||
*/
|
||||
@Override
|
||||
public void setSourceIDExtension(LCSourceIDExtension sourceIDExtension)
|
||||
{
|
||||
mSourceIdExtension = sourceIDExtension;
|
||||
|
||||
//Nullify the source address so that it can be recreated with the full source extension values.
|
||||
mSourceAddress = null;
|
||||
mIdentifiers = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Talkgroup address
|
||||
*/
|
||||
@@ -79,7 +151,15 @@ public class LCGroupVoiceChannelUser extends VoiceLinkControlMessage
|
||||
{
|
||||
if(mSourceAddress == null)
|
||||
{
|
||||
mSourceAddress = APCO25RadioIdentifier.createFrom(getInt(SOURCE_ADDRESS));
|
||||
if(isExtensionRequired() && mSourceIdExtension != null)
|
||||
{
|
||||
mSourceAddress = APCO25FullyQualifiedRadioIdentifier.createFrom(getInt(SOURCE_ADDRESS),
|
||||
mSourceIdExtension.getWACN(), mSourceIdExtension.getSystem(), mSourceIdExtension.getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
mSourceAddress = APCO25RadioIdentifier.createFrom(getInt(SOURCE_ADDRESS));
|
||||
}
|
||||
}
|
||||
|
||||
return mSourceAddress;
|
||||
@@ -90,6 +170,11 @@ public class LCGroupVoiceChannelUser extends VoiceLinkControlMessage
|
||||
*/
|
||||
public boolean hasSourceAddress()
|
||||
{
|
||||
if(isExtensionRequired() && isFullyExtended())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return getInt(SOURCE_ADDRESS) > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,16 +38,17 @@ public class LCMessageUpdateExtended extends ExtendedSourceLinkControlWord
|
||||
|
||||
private Identifier mShortDataMessage;
|
||||
private Identifier mTargetAddress;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param timestamp of the carrier message
|
||||
* @param isTerminator to indicate if message is carried by a TDULC terminator message
|
||||
*/
|
||||
public LCMessageUpdateExtended(CorrectedBinaryMessage message)
|
||||
public LCMessageUpdateExtended(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
super(message, timestamp, isTerminator);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
|
||||
@@ -22,7 +22,10 @@ package io.github.dsheirer.module.decode.p25.phase1.message.lc.standard;
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25P1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,20 +33,25 @@ import java.util.List;
|
||||
* Source ID extension word. This is used in conjunction with another link control message to carry a fully qualified
|
||||
* source SUID.
|
||||
*/
|
||||
public class LCSourceIDExtension extends LinkControlWord
|
||||
public class LCSourceIDExtension extends LinkControlWord implements IExtendedSourceMessage
|
||||
{
|
||||
private static final IntField SOURCE_SUID_WACN = IntField.length20(OCTET_2_BIT_16);
|
||||
private static final IntField SOURCE_SUID_SYSTEM = IntField.length12(OCTET_4_BIT_32 + 4);
|
||||
private static final IntField SOURCE_SUID_RADIO = IntField.length24(OCTET_6_BIT_48);
|
||||
private long mTimestamp;
|
||||
private boolean mTerminator;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param isTerminator to indicate if the carrier message is a TDULC terminator message
|
||||
*/
|
||||
public LCSourceIDExtension(CorrectedBinaryMessage message)
|
||||
public LCSourceIDExtension(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
mTimestamp = timestamp;
|
||||
mTerminator = isTerminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,6 +65,48 @@ public class LCSourceIDExtension extends LinkControlWord
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminator()
|
||||
{
|
||||
return mTerminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExtensionRequired()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceIDExtension(LCSourceIDExtension sourceIDExtension)
|
||||
{
|
||||
throw new IllegalArgumentException("Extended source cannot be attached to this message");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullyExtended()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return Protocol.APCO25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeslot()
|
||||
{
|
||||
return P25P1Message.TIMESLOT_0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source SUID WACN value.
|
||||
*/
|
||||
|
||||
@@ -41,16 +41,17 @@ public class LCStatusUpdateExtended extends ExtendedSourceLinkControlWord
|
||||
private Identifier mUnitStatus;
|
||||
private Identifier mUserStatus;
|
||||
private Identifier mTargetAddress;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param timestamp of the carrier message
|
||||
* @param isTerminator to indicate if message is carried by a TDULC terminator message
|
||||
*/
|
||||
public LCStatusUpdateExtended(CorrectedBinaryMessage message)
|
||||
public LCStatusUpdateExtended(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
super(message, timestamp, isTerminator);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
|
||||
@@ -39,16 +39,17 @@ public class LCUnitToUnitVoiceChannelUserExtended extends ExtendedSourceLinkCont
|
||||
|
||||
private VoiceServiceOptions mServiceOptions;
|
||||
private Identifier mTargetAddress;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
* @param timestamp of the carrier message
|
||||
* @param isTerminator to indicate if message is carried by a TDULC terminator message
|
||||
*/
|
||||
public LCUnitToUnitVoiceChannelUserExtended(CorrectedBinaryMessage message)
|
||||
public LCUnitToUnitVoiceChannelUserExtended(CorrectedBinaryMessage message, long timestamp, boolean isTerminator)
|
||||
{
|
||||
super(message);
|
||||
super(message, timestamp, isTerminator);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
|
||||
@@ -195,7 +195,7 @@ public class LDU1Message extends LDUMessage implements IFrequencyBandReceiver
|
||||
pointer += 6;
|
||||
}
|
||||
|
||||
mLinkControlWord = LinkControlWordFactory.create(binaryMessage);
|
||||
mLinkControlWord = LinkControlWordFactory.create(binaryMessage, getTimestamp(), false);
|
||||
mLinkControlWord.setValid(!irrecoverableErrors);
|
||||
|
||||
//If we corrected any bit errors, update the original message with the bit error count
|
||||
|
||||
@@ -160,7 +160,7 @@ public class TDULinkControlMessage extends P25P1Message implements IFrequencyBan
|
||||
pointer += 6;
|
||||
}
|
||||
|
||||
mLinkControlWord = LinkControlWordFactory.create(binaryMessage);
|
||||
mLinkControlWord = LinkControlWordFactory.create(binaryMessage, getTimestamp(), true);
|
||||
mLinkControlWord.setValid(!irrecoverableErrors);
|
||||
|
||||
//If we corrected any bit errors, update the original message with the bit error count
|
||||
|
||||
@@ -25,7 +25,6 @@ import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierClass;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
|
||||
import io.github.dsheirer.identifier.string.StringIdentifier;
|
||||
import io.github.dsheirer.identifier.tone.Tone;
|
||||
import io.github.dsheirer.identifier.tone.ToneIdentifier;
|
||||
@@ -209,8 +208,7 @@ public class AudioRecordingManager implements Listener<AudioSegment>
|
||||
|
||||
if(to != null)
|
||||
{
|
||||
String toValue = ((IntegerIdentifier)to).getValue().toString().replace(":", "");
|
||||
sb.append("_TO_").append(toValue);
|
||||
sb.append("_TO_").append(clean(to.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -218,7 +216,7 @@ public class AudioRecordingManager implements Listener<AudioSegment>
|
||||
|
||||
if(!toIdentifiers.isEmpty())
|
||||
{
|
||||
sb.append("_TO_").append(toIdentifiers.get(0));
|
||||
sb.append("_TO_").append(clean(toIdentifiers.get(0).toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,8 +224,7 @@ public class AudioRecordingManager implements Listener<AudioSegment>
|
||||
|
||||
if(from != null)
|
||||
{
|
||||
String fromValue = ((IntegerIdentifier)from).getValue().toString().replace(":", "");
|
||||
sb.append("_FROM_").append(fromValue);
|
||||
sb.append("_FROM_").append(clean(from.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -239,7 +236,7 @@ public class AudioRecordingManager implements Listener<AudioSegment>
|
||||
{
|
||||
if(identifier.getForm() != Form.TONE)
|
||||
{
|
||||
sb.append("_FROM_").append(identifier);
|
||||
sb.append("_FROM_").append(clean(identifier.toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -343,6 +340,21 @@ public class AudioRecordingManager implements Listener<AudioSegment>
|
||||
}
|
||||
}
|
||||
|
||||
public static String clean(String value)
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
return value.replace(":", "")
|
||||
.replace(".", "_")
|
||||
.replace("(", "_")
|
||||
.replace(")", "")
|
||||
.replace("ROAM ", "")
|
||||
.replace("ISSI ", "");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Threaded queue processor to process/record each recordable audio segment
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -94,7 +94,7 @@ public class AudioMetadataUtils
|
||||
if(to != null)
|
||||
{
|
||||
sb = new StringBuilder();
|
||||
sb.append(to.toString());
|
||||
sb.append(to.toString().replace("ISSI ", ""));
|
||||
|
||||
List<Alias> toAliases = aliasList.getAliases(to);
|
||||
|
||||
@@ -110,7 +110,7 @@ public class AudioMetadataUtils
|
||||
if(from != null)
|
||||
{
|
||||
sb = new StringBuilder();
|
||||
sb.append(from.toString());
|
||||
sb.append(from.toString().replace("ISSI ", "").replace("ROAM ", ""));
|
||||
|
||||
List<Alias> fromAliases = aliasList.getAliases(from);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user