diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java
index 74b21473..714d2019 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java
@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+ * Copyright (C) 2014-2019 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -84,6 +84,7 @@ import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacket
import io.github.dsheirer.module.decode.p25.message.pdu.umbtc.isp.UMBTCTelephoneInterconnectRequestExplicitDialing;
import io.github.dsheirer.module.decode.p25.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.message.tsbk.TSBKMessage;
+import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.MotorolaDenyResponse;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrantUpdate;
import io.github.dsheirer.module.decode.p25.message.tsbk.standard.isp.CancelServiceRequest;
@@ -2134,9 +2135,22 @@ public class P25DecoderState extends DecoderState implements IChannelEventListen
.identifiers(icRoaming)
.build());
break;
- case MOTOROLA_OSP_OPCODE_7:
- mLog.info("MOTOROLA OPCODE 7: " + tsbk.getMessage().toHexString());
- break;
+ case MOTOROLA_OSP_DENY_RESPONSE:
+ if(tsbk instanceof MotorolaDenyResponse)
+ {
+ MotorolaDenyResponse dr = (MotorolaDenyResponse)tsbk;
+ MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
+ ic.remove(IdentifierClass.USER);
+ ic.update(tsbk.getIdentifiers());
+
+ broadcast(P25DecodeEvent.builder(tsbk.getTimestamp())
+ .channel(getCurrentChannel())
+ .eventDescription(DecodeEventType.RESPONSE.toString())
+ .details("DENY: " + dr.getDeniedServiceType().getDescription() +
+ " REASON: " + dr.getDenyReason() + " - INFO: " + dr.getAdditionalInfo())
+ .identifiers(ic)
+ .build());
+ }
default:
// mLog.debug("Unrecognized TSBK Opcode: " + tsbk.getOpcode().name() + " VENDOR:" + tsbk.getVendor() +
// " OPCODE:" + tsbk.getOpcodeNumber());
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java
index cd446fd0..8135cb2d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/Opcode.java
@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+ * Copyright (C) 2014-2019 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -170,7 +170,7 @@ public enum Opcode
MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT(2, "PTCH GRP VCHN GR", "MOTOROLA PATCH GROUP CHANNEL GRANT"),
MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE(3, "PTCH GRP VCH UPD", "MOTOROLA PATCH GROUP CHANNEL GRANT UPDATE"),
MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "CHANNEL CWID", "CHANNEL CWID"),
- MOTOROLA_OSP_OPCODE_7(7, "MOTOROLA OPCODE 7", "MOTOROLA OPCODE 07"),
+ MOTOROLA_OSP_DENY_RESPONSE(7, "DENY RESPONSE", "MOTOROLA DENY RESPONSE"),
MOTOROLA_OSP_SYSTEM_LOADING(9, "SYSTEM LOADING", "SYSTEM LOADING"),
MOTOROLA_OSP_CONTROL_CHANNEL_ID(11, "CCH BASE STAT ID", "CONTROL CHANNEL BASE STATION ID"),
MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN(14, "CCH PLND SHUTDWN", "CONTROL CHANNEL PLANNED SHUTDOWN"),
@@ -294,7 +294,7 @@ public enum Opcode
case 0x05:
return MOTOROLA_OSP_TRAFFIC_CHANNEL_ID;
case 0x07:
- return MOTOROLA_OSP_OPCODE_7;
+ return MOTOROLA_OSP_DENY_RESPONSE;
case 0x09:
return MOTOROLA_OSP_SYSTEM_LOADING;
case 0x0B:
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java
index b2e0503a..c23d8f9c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/TSBKMessageFactory.java
@@ -1,3 +1,23 @@
+/*
+ * ******************************************************************************
+ * sdrtrunk
+ * Copyright (C) 2014-2019 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * *****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.p25.message.tsbk;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
@@ -6,6 +26,7 @@ import io.github.dsheirer.module.decode.p25.P25Interleave;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.isp.UnknownMotorolaISPMessage;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.ChannelCWID;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.ChannelLoading;
+import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.MotorolaDenyResponse;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupAdd;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupDelete;
import io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp.PatchGroupVoiceChannelGrant;
@@ -270,6 +291,8 @@ public class TSBKMessageFactory
return new ChannelCWID(dataUnitID, message, nac, timestamp);
case MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN:
return new PlannedChannelShutdown(dataUnitID, message, nac, timestamp);
+ case MOTOROLA_OSP_DENY_RESPONSE:
+ return new MotorolaDenyResponse(dataUnitID, message, nac, timestamp);
case MOTOROLA_OSP_TRAFFIC_CHANNEL_ID:
return new ChannelCWID(dataUnitID, message, nac, timestamp);
case MOTOROLA_OSP_PATCH_GROUP_ADD:
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java
new file mode 100644
index 00000000..1ec4b41e
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/message/tsbk/motorola/osp/MotorolaDenyResponse.java
@@ -0,0 +1,132 @@
+/*
+ * ******************************************************************************
+ * sdrtrunk
+ * Copyright (C) 2014-2019 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * *****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.p25.message.tsbk.motorola.osp;
+
+import io.github.dsheirer.bits.CorrectedBinaryMessage;
+import io.github.dsheirer.identifier.Identifier;
+import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25ToTalkgroup;
+import io.github.dsheirer.module.decode.p25.message.tsbk.OSPMessage;
+import io.github.dsheirer.module.decode.p25.message.tsbk.Opcode;
+import io.github.dsheirer.module.decode.p25.reference.DataUnitID;
+import io.github.dsheirer.module.decode.p25.reference.DenyReason;
+import io.github.dsheirer.module.decode.p25.reference.Direction;
+import io.github.dsheirer.module.decode.p25.reference.Vendor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Deny response
+ */
+public class MotorolaDenyResponse extends OSPMessage
+{
+ private static final int ADDITIONAL_INFORMATION_FLAG = 16;
+ private static final int[] SERVICE_TYPE = {18, 19, 20, 21, 22, 23};
+ private static final int[] REASON = {24, 25, 26, 27, 28, 29, 30, 31};
+ private static final int[] ADDITIONAL_INFO = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55};
+ private static final int[] TARGET_ADDRESS = {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79};
+
+ private DenyReason mDenyReason;
+ private String mAdditionalInfo;
+ private Identifier mTargetAddress;
+ private List mIdentifiers;
+
+ /**
+ * Constructs a TSBK from the binary message sequence.
+ */
+ public MotorolaDenyResponse(DataUnitID dataUnitId, CorrectedBinaryMessage message, int nac, long timestamp)
+ {
+ super(dataUnitId, message, nac, timestamp);
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getMessageStub());
+ sb.append(" TO:").append(getTargetAddress());
+ sb.append(" SERVICE:").append(getDeniedServiceType());
+ sb.append(" REASON:").append(getDenyReason());
+
+ if(hasAdditionalInformation())
+ {
+ sb.append(" INFO:").append(getAdditionalInfo());
+ }
+
+ return sb.toString();
+ }
+
+ private boolean hasAdditionalInformation()
+ {
+ return getMessage().get(ADDITIONAL_INFORMATION_FLAG);
+ }
+
+ public String getAdditionalInfo()
+ {
+ if(mAdditionalInfo == null)
+ {
+ mAdditionalInfo = getMessage().getHex(ADDITIONAL_INFO, 6);
+ }
+
+ return mAdditionalInfo;
+ }
+
+ /**
+ * Opcode representing the service type that is being acknowledged by the radio unit.
+ */
+ public Opcode getDeniedServiceType()
+ {
+ return Opcode.fromValue(getMessage().getInt(SERVICE_TYPE), Direction.INBOUND, Vendor.STANDARD);
+ }
+
+ public DenyReason getDenyReason()
+ {
+ if(mDenyReason == null)
+ {
+ mDenyReason = DenyReason.fromCode(getMessage().getInt(REASON));
+ }
+
+ return mDenyReason;
+ }
+
+ public Identifier getTargetAddress()
+ {
+ if(mTargetAddress == null)
+ {
+ mTargetAddress = APCO25ToTalkgroup.createIndividual(getMessage().getInt(TARGET_ADDRESS));
+ }
+
+ return mTargetAddress;
+ }
+
+ @Override
+ public List getIdentifiers()
+ {
+ if(mIdentifiers == null)
+ {
+ mIdentifiers = new ArrayList<>();
+ mIdentifiers.add(getTargetAddress());
+ }
+
+ return mIdentifiers;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java
index 666141b3..57c8c1d0 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/reference/DenyReason.java
@@ -1,125 +1,155 @@
+/*
+ * ******************************************************************************
+ * sdrtrunk
+ * Copyright (C) 2014-2019 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * *****************************************************************************
+ */
+
package io.github.dsheirer.module.decode.p25.reference;
public enum DenyReason
{
- RESERVED( 0x00 ),
- REQUESTING_UNIT_NOT_VALID( 0x10 ),
- REQUESTING_UNIT_NOT_AUTHORIZED_FOR_SERVICE( 0x11 ),
- TARGET_UNIT_NOT_VALID( 0x20 ),
- TARGET_UNIT_NOT_AUTHORIZED_FOR_SERVICE( 0x21 ),
- TARGET_UNIT_REFUSED_CALL( 0x2F ),
- TARGET_GROUP_NOT_VALID( 0x30 ),
- TARGET_GROUP_NOT_AUTHORIZED_FOR_SERVICE( 0x31 ),
- INVALID_DIALING( 0x40 ),
- TELEPHONE_NUMBER_NOT_AUTHORIZED( 0x41 ),
- PSTN_NOT_VALID( 0x42 ),
- CALL_TIMEOUT( 0x50 ),
- LANDLINE_TERMINATED_CALL( 0x51 ),
- SUBSCRIBER_UNIT_TERMINATED_CALL( 0x52 ),
- CALL_PREEMPTED( 0x5F ),
- SITE_ACCESS_DENIAL( 0x60 ),
- USER_OR_SYSTEM_DEFINED( 0x61 ),
- CALL_OPTIONS_NOT_VALID_FOR_SERVICE( 0xF0 ),
- PROTECTION_SERVICE_OPTION_NOT_VALID( 0xF1 ),
- DUPLEX_SERVICE_OPTION_NOT_VALID( 0xF2 ),
- CIRCUIT_OR_PACKET_MODE_OPTION_NOT_VALID( 0xF3 ),
- SYSTEM_DOES_NOT_SUPPORT_SERVICE( 0xFF ),
- UNKNOWN( -1 );
-
+ RESERVED(0x00),
+ REQUESTING_UNIT_NOT_VALID(0x10),
+ REQUESTING_UNIT_NOT_AUTHORIZED_FOR_SERVICE(0x11),
+ TARGET_UNIT_NOT_VALID(0x20),
+ TARGET_UNIT_NOT_AUTHORIZED_FOR_SERVICE(0x21),
+ TARGET_UNIT_REFUSED_CALL(0x2F),
+ TARGET_GROUP_NOT_VALID(0x30),
+ TARGET_GROUP_NOT_AUTHORIZED_FOR_SERVICE(0x31),
+ INVALID_DIALING(0x40),
+ TELEPHONE_NUMBER_NOT_AUTHORIZED(0x41),
+ PSTN_NOT_VALID(0x42),
+ CALL_TIMEOUT(0x50),
+ LANDLINE_TERMINATED_CALL(0x51),
+ SUBSCRIBER_UNIT_TERMINATED_CALL(0x52),
+ CALL_PREEMPTED(0x5F),
+ SITE_ACCESS_DENIAL(0x60),
+ USER_OR_SYSTEM_DEFINED(0x61), //0x61 - 0xEF
+ PTT_COLLIDE(0x67),
+ PTT_BONK(0x77),
+ CALL_OPTIONS_NOT_VALID_FOR_SERVICE(0xF0),
+ PROTECTION_SERVICE_OPTION_NOT_VALID(0xF1),
+ DUPLEX_SERVICE_OPTION_NOT_VALID(0xF2),
+ CIRCUIT_OR_PACKET_MODE_OPTION_NOT_VALID(0xF3),
+ SYSTEM_DOES_NOT_SUPPORT_SERVICE(0xFF),
+ UNKNOWN(-1);
+
private int mCode;
-
- private DenyReason( int code )
+
+ private DenyReason(int code)
{
mCode = code;
}
-
- public static DenyReason fromCode( int code )
+
+ public static DenyReason fromCode(int code)
{
- if( code == 0x10 )
+ if(code == 0x10)
{
return DenyReason.REQUESTING_UNIT_NOT_VALID;
}
- else if( code == 0x11 )
+ else if(code == 0x11)
{
return REQUESTING_UNIT_NOT_AUTHORIZED_FOR_SERVICE;
}
- else if( code == 0x20 )
+ else if(code == 0x20)
{
return DenyReason.TARGET_UNIT_NOT_VALID;
}
- else if( code == 0x21 )
+ else if(code == 0x21)
{
return TARGET_UNIT_NOT_AUTHORIZED_FOR_SERVICE;
}
- else if( code == 0x2F )
+ else if(code == 0x2F)
{
return DenyReason.TARGET_UNIT_REFUSED_CALL;
}
- else if( code == 0x30 )
+ else if(code == 0x30)
{
return TARGET_GROUP_NOT_VALID;
}
- else if( code == 0x31 )
+ else if(code == 0x31)
{
return DenyReason.TARGET_GROUP_NOT_AUTHORIZED_FOR_SERVICE;
}
- else if( code == 0x40 )
+ else if(code == 0x40)
{
return DenyReason.INVALID_DIALING;
}
- else if( code == 0x41 )
+ else if(code == 0x41)
{
return DenyReason.TELEPHONE_NUMBER_NOT_AUTHORIZED;
}
- else if( code == 0x42 )
+ else if(code == 0x42)
{
return DenyReason.PSTN_NOT_VALID;
}
- else if( code == 0x50 )
+ else if(code == 0x50)
{
return CALL_TIMEOUT;
}
- else if( code == 0x51 )
+ else if(code == 0x51)
{
return LANDLINE_TERMINATED_CALL;
}
- else if( code == 0x52 )
+ else if(code == 0x52)
{
return SUBSCRIBER_UNIT_TERMINATED_CALL;
}
- else if( code == 0x5F )
+ else if(code == 0x5F)
{
return DenyReason.CALL_PREEMPTED;
}
- else if( code == 0x60 )
+ else if(code == 0x60)
{
return SITE_ACCESS_DENIAL;
}
- else if( code == 0xF0 )
+ else if(code == 0xF0)
{
return CALL_OPTIONS_NOT_VALID_FOR_SERVICE;
}
- else if( code == 0xF1 )
+ else if(code == 0xF1)
{
return PROTECTION_SERVICE_OPTION_NOT_VALID;
}
- else if( code == 0xF2 )
+ else if(code == 0xF2)
{
return DenyReason.DUPLEX_SERVICE_OPTION_NOT_VALID;
}
- else if( code == 0xF3 )
+ else if(code == 0xF3)
{
return CIRCUIT_OR_PACKET_MODE_OPTION_NOT_VALID;
}
- else if( code <= 0x5E )
+ else if(code == 0x67)
+ {
+ return PTT_COLLIDE;
+ }
+ else if(code == 0x77)
+ {
+ return PTT_BONK;
+ }
+ else if(code <= 0x5E)
{
return DenyReason.RESERVED;
}
- else if( code >= 0x61 )
+ else if(code >= 0x61)
{
return DenyReason.USER_OR_SYSTEM_DEFINED;
}
-
+
return UNKNOWN;
}
}