Skip to content

Commit

Permalink
#2004 P25P1 Link Control Extended Source messages now process through…
Browse files Browse the repository at this point in the history
… decoder state correctly and display in Now Playing.
  • Loading branch information
Dennis Sheirer committed Oct 5, 2024
1 parent c97e355 commit c1e817e
Show file tree
Hide file tree
Showing 18 changed files with 360 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ public Protocol getProtocol()
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -51,8 +47,14 @@ public Protocol getProtocol()
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
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;
Expand Down Expand Up @@ -318,6 +319,10 @@ public void receive(IMessage iMessage)
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -110,18 +111,26 @@ public void receive(IMessage message)
//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();
}
Expand Down Expand Up @@ -168,7 +177,10 @@ else if(message instanceof TDULinkControlMessage tdu)

if(mMessageListener != null)
{
mMessageListener.receive(message);
if(message != null)
{
mMessageListener.receive(message);
}

if(additionalMessageToSend != null)
{
Expand All @@ -179,26 +191,32 @@ else if(message instanceof TDULinkControlMessage tdu)

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

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,63 @@

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

/**
Expand All @@ -59,6 +94,26 @@ protected LCSourceIDExtension getSourceIDExtension()
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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -104,19 +106,19 @@ public static LinkControlWord create(CorrectedBinaryMessage message)
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:
return new LCGroupVoiceChannelUpdateExplicit(message);
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:
Expand All @@ -132,13 +134,13 @@ public static LinkControlWord create(CorrectedBinaryMessage message)
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:
Expand All @@ -154,7 +156,7 @@ public static LinkControlWord create(CorrectedBinaryMessage message)
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -55,7 +53,7 @@ public void reset()
* @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;

Expand Down
Loading

0 comments on commit c1e817e

Please sign in to comment.