Skip to content

Commit

Permalink
#1977 P25 FQSUID handling improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dennis Sheirer committed Sep 14, 2024
1 parent 5e56bd5 commit a289fe1
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@

import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.Callback;

/**
* JavaFX identifier collection viewer
Expand Down Expand Up @@ -82,7 +85,19 @@ public TableView<Identifier> getIdentifierTableView()
valueColumn.setText("Value");
valueColumn.setCellValueFactory(new PropertyValueFactory<>("value"));

mIdentifierTableView.getColumns().addAll(classColumn, formColumn, roleColumn, valueColumn);
TableColumn textColumn = new TableColumn();
textColumn.setPrefWidth(160);
textColumn.setText("Text");
textColumn.setCellValueFactory((Callback<TableColumn.CellDataFeatures<Identifier<?>, String>, ObservableValue>) param -> {
if(param.getValue() != null)
{
return new SimpleStringProperty(param.getValue().toString());
}

return null;
});

mIdentifierTableView.getColumns().addAll(classColumn, formColumn, roleColumn, valueColumn, textColumn);
}

return mIdentifierTableView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public String getFullyQualifiedRadioAddress()
*/
public boolean isAliased()
{
return getValue() != mRadio;
return getValue() != 0 && getValue() != mRadio;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public String getFullyQualifiedTalkgroupAddress()
*/
public boolean isAliased()
{
return getValue() != mTalkgroup;
return getValue() != 0 && getValue() != mTalkgroup;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
import io.github.dsheirer.identifier.patch.PatchGroupPreLoadDataContent;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.identifier.scramble.ScrambleParameterIdentifier;
import io.github.dsheirer.log.LoggingSuppressor;
import io.github.dsheirer.message.IMessage;
Expand Down Expand Up @@ -307,7 +306,7 @@ public void processP2ChannelUpdate(APCO25Channel channel, ServiceOptions service
* @param timeslot for the channel.
* @param isIdleNull to indicate if this close event is trigged by an IDLE/NULL message
*/
public void closeP2CallEvent(long frequency, int timeslot, long timestamp, boolean isIdleNull)
public void closeP2CallEvent(long frequency, int timeslot, boolean isIdleNull)
{
/**
* Hack: L3Harris systems can issue a channel grant on control/TS1 which creates an event for TS2 and then the
Expand All @@ -325,21 +324,13 @@ public void closeP2CallEvent(long frequency, int timeslot, long timestamp, boole

try
{
DecodeEvent event;

if(timeslot == P25P1Message.TIMESLOT_0 || timeslot == P25P1Message.TIMESLOT_1)
{
event = mTS1ChannelGrantEventMap.remove(frequency);
mTS1ChannelGrantEventMap.remove(frequency);
}
else
{
event = mTS2ChannelGrantEventMap.remove(frequency);
}

if(event != null)
{
event.end(timestamp);
broadcast(event);
mTS2ChannelGrantEventMap.remove(frequency);
}
}
finally
Expand Down Expand Up @@ -1410,55 +1401,50 @@ public void removeChannelEventListener()
* Compares the TO role identifier(s) from each collection for equality. This is normally used to compare a call
* update where we only compare the TO role.
*
* @param collection1 containing a TO identifier
* @param collection2 containing a TO identifier
* @param existingIC containing a TO identifier
* @param nextIC containing a TO identifier
* @return true if both collections contain a TO identifier and the TO identifiers are the same value
*/
private boolean isSameCallUpdate(IdentifierCollection collection1, IdentifierCollection collection2)
private boolean isSameCallUpdate(IdentifierCollection existingIC, IdentifierCollection nextIC)
{
Identifier toIdentifier1 = collection1.getToIdentifier();
Identifier toIdentifier2 = collection2.getToIdentifier();
return toIdentifier1 != null && toIdentifier1.equals(toIdentifier2);
Identifier existingTO = existingIC.getToIdentifier();
Identifier nextTO = nextIC.getToIdentifier();
return existingTO != null && existingTO.equals(nextTO);
}

/**
* Compares the TO role identifier(s) from each collection for equality. This is normally used to compare a call
* update where we only compare the TO role.
*
* @param collection1 containing a TO identifier
* @param collection2 containing a TO identifier
* @param existingIC for the existing/previous event containing a TO identifier
* @param nextIC for the next event containing a TO identifier
* @return true if both collections contain a TO identifier and the TO identifiers are the same value
*/
private boolean isSameCallFull(IdentifierCollection collection1, IdentifierCollection collection2)
private boolean isSameCallFull(IdentifierCollection existingIC, IdentifierCollection nextIC)
{
Identifier to1 = collection1.getToIdentifier();
Identifier to2 = collection2.getToIdentifier();
Identifier existingTO = existingIC.getToIdentifier();
Identifier nextTO = nextIC.getToIdentifier();

if(to1 != null && to1.equals(to2))
if(existingTO != null && existingTO.equals(nextTO))
{
Identifier from1 = collection1.getFromIdentifier();
Identifier existingFROM = existingIC.getFromIdentifier();

//If the FROM identifier hasn't yet been established, then this is the same call. We also ignore the
//talker alias as a call identifier since on L3Harris systems they can transmit the talker alias before
//they transmit the radio ID.
if(from1 == null || from1.getForm() == Form.TALKER_ALIAS)
if(existingFROM == null || existingFROM.getForm() == Form.TALKER_ALIAS)
{
return true;
}

Identifier from2 = collection2.getFromIdentifier();

if(from2 != null && from2.getForm() == Form.TALKER_ALIAS)
{
return true;
}
Identifier nextFROM = nextIC.getFromIdentifier();

if(from2 instanceof RadioIdentifier radio && radio.getValue() == 0)
if(nextFROM != null && nextFROM.getForm() == Form.TALKER_ALIAS)
{
return true;
}

return from1.equals(from2);
return existingFROM.equals(nextFROM);
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void reset()
protected void resetState()
{
super.resetState();
closeCurrentCallEvent(System.currentTimeMillis(), true, false);
closeCurrentCallEvent(true, false);
mEndPttOnFacchCounter = 0;
}

Expand Down Expand Up @@ -1274,7 +1274,7 @@ private void processEndPushToTalk(MacMessage message, MacStructure mac)
{
if(mac instanceof EndPushToTalk)
{
closeCurrentCallEvent(message.getTimestamp(), true, false);
closeCurrentCallEvent(true, false);

if(message.getDataUnitID().isFACCH())
{
Expand Down Expand Up @@ -1561,7 +1561,7 @@ private void processMacRelease(MacMessage message, MacStructure mac)
{
if(mac instanceof MacRelease mr)
{
closeCurrentCallEvent(message.getTimestamp(), true, false);
closeCurrentCallEvent(true, false);
broadcast(message, mac, DecodeEventType.COMMAND,
(mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" +
(mr.isTalkerPreemption() ? " BY USER" : " BY CONTROLLER"));
Expand Down Expand Up @@ -1882,13 +1882,12 @@ private void updateCurrentCall(MacOpcode macOpcode, ServiceOptions serviceOption
/**
* Ends/closes the current call event.
*
* @param timestamp of the message that indicates the event has ended.
* @param resetIdentifiers to reset the FROM/TO identifiers (true) or reset just the FROM identifiers (false)
* @param isIdleNull to indicate if the calling trigger is an IDLE/NULL message
*/
private void closeCurrentCallEvent(long timestamp, boolean resetIdentifiers, boolean isIdleNull)
private void closeCurrentCallEvent(boolean resetIdentifiers, boolean isIdleNull)
{
mTrafficChannelManager.closeP2CallEvent(getCurrentFrequency(), getTimeslot(), timestamp, isIdleNull);
mTrafficChannelManager.closeP2CallEvent(getCurrentFrequency(), getTimeslot(), isIdleNull);

if(resetIdentifiers)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupChannelGrantUpdate;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupDeleteCommand;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupExtendedFunctionCommand;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupOpcode145;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupTalkerAlias;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupTalkerAliasContinuation;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUpdate;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUserAbbreviated;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupVoiceChannelUserExtended;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaOpcode149;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaQueuedResponse;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.ArrayList;
Expand Down Expand Up @@ -585,9 +585,9 @@ public static MacStructure createMacStructure(CorrectedBinaryMessage message, in
case MOTOROLA_89_GROUP_REGROUP_DELETE:
return new MotorolaGroupRegroupDeleteCommand(message, offset);
case MOTOROLA_91_GROUP_REGROUP_UNKNOWN:
return new MotorolaGroupRegroupOpcode145(message, offset);
return new MotorolaGroupRegroupTalkerAlias(message, offset);
case MOTOROLA_95_UNKNOWN_149:
return new MotorolaOpcode149(message, offset);
return new MotorolaGroupRegroupTalkerAliasContinuation(message, offset);
case MOTOROLA_A0_GROUP_REGROUP_VOICE_CHANNEL_USER_EXTENDED:
return new MotorolaGroupRegroupVoiceChannelUserExtended(message, offset);
case MOTOROLA_A3_GROUP_REGROUP_CHANNEL_GRANT_IMPLICIT:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
import java.util.List;

/**
* Motorola Group Regroup Unknown Opcode 145 - 17-bytes long
* Motorola Group Regroup Talker Alias - 17-bytes long
*/
public class MotorolaGroupRegroupOpcode145 extends MacStructureVendor
public class MotorolaGroupRegroupTalkerAlias extends MacStructureVendor
{
private static final IntField TALKGROUP = IntField.length16(24);
private static final IntField UNKNOWN = IntField.length32(40);
Expand All @@ -49,7 +49,7 @@ public class MotorolaGroupRegroupOpcode145 extends MacStructureVendor
* @param message containing the message bits
* @param offset into the message for this structure
*/
public MotorolaGroupRegroupOpcode145(CorrectedBinaryMessage message, int offset)
public MotorolaGroupRegroupTalkerAlias(CorrectedBinaryMessage message, int offset)
{
super(message, offset);
}
Expand All @@ -60,7 +60,7 @@ public MotorolaGroupRegroupOpcode145(CorrectedBinaryMessage message, int offset)
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("MOTOROLA GROUP REGROUP UNKNOWN OPCODE 145 TALKGROUP:").append(getTalkgroup());
sb.append("MOTOROLA GROUP REGROUP TALKER ALIAS TALKGROUP:").append(getTalkgroup());
sb.append(" SOURCE SUID RADIO:").append(getRadio());
sb.append(" UNK:").append(Integer.toHexString(getInt(UNKNOWN)).toUpperCase());
sb.append(" MSG:").append(getMessage().get(getOffset(), getMessage().length()).toHexString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,30 @@
import java.util.List;

/**
* Motorola Unknown Opcode 149 (0x95)
* Motorola Unknown Opcode 149 (0x95) Talker Alias continuation message.
*
* This was observed at the end of a Group Regroup call sequence during HANGTIME, so it seems more of an announcement
* type message and less as something relevant to the call.
* This was observed at the end of a Group Regroup call sequence during HANGTIME. Each radio doesn't know the alias
* until it receives it once and then queues the alias for subsequent use.
*
* This opcode is observed following an Opcode 145 message and seems to be a continuation message.
*
* Examples:
* 959011 018E58B82BAB4D3B70E9A8457F9D C67
* 959011 0287000000000000000000000000 E26
*
* The opcode, vendor and length octets are consistent. The first octet seems to be an identifier, 01, 02, etc.
* Example 2:
* 9190110BCD02010026BEE004A403151724FD9 Opcode 145
* 959011012436C4C022EC902A9943EDAA69E76 Opcode 149
* 95901102298FAA77683FAE0000000000000AB Opcode 149
* (immediately followed in the same call sequence by a slight variation)
* 9190110BCD02010036BEE004A403151724513 Opcode 145
* 959011013436C4C022EC902A9943EDAA699F6 Opcode 149
* 95901102398FAA77683FAE00000000000072B Opcode 149
*
* The opcode, vendor and length octets are consistent. The first octet seems to be a sequence identifier, 01, 02, etc.
* Nothing else seems to match any of the other identifiers that were active at call time.
*/
public class MotorolaOpcode149 extends MacStructureVendor
public class MotorolaGroupRegroupTalkerAliasContinuation extends MacStructureVendor
{
private List<Identifier> mIdentifiers;

Expand All @@ -48,7 +59,7 @@ public class MotorolaOpcode149 extends MacStructureVendor
* @param message containing the message bits
* @param offset into the message for this structure
*/
public MotorolaOpcode149(CorrectedBinaryMessage message, int offset)
public MotorolaGroupRegroupTalkerAliasContinuation(CorrectedBinaryMessage message, int offset)
{
super(message, offset);
}
Expand All @@ -59,7 +70,7 @@ public MotorolaOpcode149(CorrectedBinaryMessage message, int offset)
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("MOTOROLA UNKNOWN OPCODE 149");
sb.append("MOTOROLA TALKER ALIAS CONTINUATION");
sb.append(" MSG:").append(getMessage().get(getOffset(), getMessage().length()).toHexString());
return sb.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,30 @@ public static String format(FullyQualifiedRadioIdentifier identifier, IntegerFor
{
case DECIMAL:
case FORMATTED:
sb.append(toDecimal(id, RADIO_DECIMAL_WIDTH));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
if(id > 0)
{
sb.append(toDecimal(id, RADIO_DECIMAL_WIDTH)).append(" (");
}
sb.append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toDecimal(radio, RADIO_DECIMAL_WIDTH)).append(")");
sb.append(".").append(toDecimal(radio, RADIO_DECIMAL_WIDTH));
if(id > 0)
{
sb.append(")");
}
return sb.toString();
case HEXADECIMAL:
sb.append(toHex(id, RADIO_HEXADECIMAL_WIDTH));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
if(id > 0)
{
sb.append(toHex(id, RADIO_HEXADECIMAL_WIDTH)).append(" (");
}
sb.append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(radio, RADIO_HEXADECIMAL_WIDTH)).append(")");
sb.append(".").append(toHex(radio, RADIO_HEXADECIMAL_WIDTH));
if(id > 0)
{
sb.append(")");
}
return sb.toString();
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
Expand All @@ -248,16 +262,30 @@ public static String format(FullyQualifiedRadioIdentifier identifier, IntegerFor
{
case DECIMAL:
case FORMATTED:
sb.append(id);
if(id > 0)
{
sb.append(id).append(" (");
}
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(radio).append(")");
sb.append(".").append(radio);
if(id > 0)
{
sb.append(")");
}
return sb.toString();
case HEXADECIMAL:
sb.append(toHex(id));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
if(id > 0)
{
sb.append(toHex(id)).append(" (");
}
sb.append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(radio)).append(")");
sb.append(".").append(toHex(radio));
if(id > 0)
{
sb.append(")");
}
return sb.toString();
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
Expand Down
Loading

0 comments on commit a289fe1

Please sign in to comment.