#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:
Denny Sheirer
2024-10-05 08:53:27 -04:00
committed by GitHub
parent c97e355349
commit d24895575c
18 changed files with 360 additions and 63 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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());

View File

@@ -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;
}
/**

View File

@@ -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;
}
/**

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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.
*/

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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);