getIdentifiers()
+ {
+ if(mIdentifiers == null)
+ {
+ mIdentifiers = new ArrayList<>();
+ if(hasDestination())
+ {
+ mIdentifiers.add(getDestination());
+ }
+ }
+
+ return mIdentifiers;
+ }
+
+ @Override
+ public IPacket getPayload()
+ {
+ return null;
+ }
+
+ @Override
+ public boolean hasPayload()
+ {
+ return false;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/DestinationId.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/DestinationId.java
index 6e442f73..6c84e682 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/DestinationId.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/DestinationId.java
@@ -20,6 +20,7 @@
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
+import io.github.dsheirer.identifier.ipv4.IPV4Address;
/**
* Destination Identifier
@@ -45,10 +46,19 @@ public class DestinationId extends HyteraToken
public String toString()
{
StringBuilder sb = new StringBuilder();
- sb.append("DESTINATION:").append(getId());
+ sb.append("DESTINATION:").append(getIdFormatted());
return sb.toString();
}
+ /**
+ * Indicates if this is an IP address (true) or a Radio ID (false).
+ * @return true if IP
+ */
+ public boolean isIp()
+ {
+ return getId() > 0x00FFFFFF;
+ }
+
/**
* Length of the content field in bytes.
* @return content field length.
@@ -69,6 +79,18 @@ public class DestinationId extends HyteraToken
return mMessage.getInt(ID);
}
+ public String getIdFormatted()
+ {
+ if(isIp())
+ {
+ return new IPV4Address(getId()).toString() + " [" + (getId() & 0xFFFFFF) + "]";
+ }
+ else
+ {
+ return String.valueOf(getId());
+ }
+ }
+
@Override
HyteraTokenType getTokenType()
{
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenFactory.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenFactory.java
index 7bf3f6b5..0ce6c965 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenFactory.java
@@ -37,11 +37,13 @@ public class HyteraTokenFactory
return switch(token)
{
case ID_DESTINATION -> new DestinationId(message);
- case ENCODING -> new Encoding(message);
case ID_MESSAGE -> new MessageId(message);
- case PAYLOAD -> new Payload(message);
- case MESSAGE_HEADER -> new MessageHeader(message);
case ID_SOURCE -> new SourceId(message);
+ case ENCODING -> new Encoding(message);
+ case PAYLOAD -> new Payload(message);
+ case RRS_UNKNOWN -> new RrsUnknown(message);
+ case TEXT_MESSAGE_SERVICE_HEADER -> new TmsMessageHeader(message);
+ case RADIO_REGISTRATION_SERVICE_HEADER -> new RrsMessageHeader(message);
default -> new Unknown(message);
};
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenHeader.java
index 0fd0c82c..7d50ce5d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenHeader.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenHeader.java
@@ -72,7 +72,15 @@ public class HyteraTokenHeader implements IHeader
*/
public boolean isSMSMessage()
{
- return hasTokens(HyteraTokenType.MESSAGE_HEADER, HyteraTokenType.ENCODING, HyteraTokenType.PAYLOAD);
+ return hasTokens(HyteraTokenType.TEXT_MESSAGE_SERVICE_HEADER, HyteraTokenType.ENCODING, HyteraTokenType.PAYLOAD);
+ }
+
+ /**
+ * Indicates if this header has RRS service message.
+ */
+ public boolean isRRSMessage()
+ {
+ return hasTokens(HyteraTokenType.RADIO_REGISTRATION_SERVICE_HEADER, HyteraTokenType.ID_DESTINATION);
}
/**
@@ -153,7 +161,7 @@ public class HyteraTokenHeader implements IHeader
length += contentLength * 8; //Actual content
}
- HyteraToken field = HyteraTokenFactory.getField(token, mMessage.getSubMessage(offset, offset + length + 1));
+ HyteraToken field = HyteraTokenFactory.getField(token, mMessage.getSubMessage(offset, offset + length));
offset += length;
mParsedTokens.add(field);
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenType.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenType.java
index 03c34f9e..effd8a28 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenType.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/HyteraTokenType.java
@@ -29,13 +29,15 @@ package io.github.dsheirer.module.decode.ip.hytera.sds;
*/
public enum HyteraTokenType
{
- //Note: this may be the message type, but is definitely followed by 4-byte total length field. Need more examples
- MESSAGE_HEADER(0x0201, 4), //First 2 bytes are total message length, 2nd 2 bytes = unknown
+ RADIO_REGISTRATION_SERVICE_HEADER(0x0200, 1), //Total message length
+ TEXT_MESSAGE_SERVICE_HEADER(0x0201, 4), //First 2 bytes are total message length, 2nd 2 bytes = unknown
+
ID_MESSAGE(0x0001, -1),
ID_SOURCE(0x0002, -1),
ID_DESTINATION(0x0003, -1),
ENCODING(0x0101, -1),
PAYLOAD(0x0102, -1),
+ RRS_UNKNOWN(0x0703, 0),
UNKNOWN(-1, -1);
private int mOpcode;
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/RrsMessageHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/RrsMessageHeader.java
new file mode 100644
index 00000000..dfbe8ff2
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/RrsMessageHeader.java
@@ -0,0 +1,66 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 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.ip.hytera.sds;
+
+import io.github.dsheirer.bits.CorrectedBinaryMessage;
+
+/**
+ * Radio Registration Service (RRS) Message Header
+ */
+public class RrsMessageHeader extends HyteraToken
+{
+ private static int[] MESSAGE_LENGTH_BCD_TENS = new int[]{16, 17, 18, 19};
+ private static int[] MESSAGE_LENGTH_BCD_ONES = new int[]{20, 21, 22, 23};
+
+ /**
+ * Constructs an instance
+ *
+ * @param message containing both the token, run-length, and content
+ */
+ public RrsMessageHeader(CorrectedBinaryMessage message)
+ {
+ super(message);
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("RRS HEADER LENGTH:").append(getMessageLength());
+ return sb.toString();
+ }
+
+ @Override
+ HyteraTokenType getTokenType()
+ {
+ return HyteraTokenType.RADIO_REGISTRATION_SERVICE_HEADER;
+ }
+
+ /**
+ * Overall message length in bytes
+ * @return message length.
+ */
+ public int getMessageLength()
+ {
+ int length = mMessage.getInt(MESSAGE_LENGTH_BCD_TENS) * 10;
+ length += mMessage.getInt(MESSAGE_LENGTH_BCD_ONES);
+ return length;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/RrsUnknown.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/RrsUnknown.java
new file mode 100644
index 00000000..da568240
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/RrsUnknown.java
@@ -0,0 +1,50 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 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.ip.hytera.sds;
+
+import io.github.dsheirer.bits.CorrectedBinaryMessage;
+
+/**
+ * Unknown RRS field parsed from a Hytera Radio Registration Service (RRS) message
+ */
+public class RrsUnknown extends HyteraToken
+{
+ /**
+ * Constructs an instance
+ *
+ * @param message containing both the token, run-length, and content
+ */
+ public RrsUnknown(CorrectedBinaryMessage message)
+ {
+ super(message);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "RRS UNKNOWN TOKEN";
+ }
+
+ @Override
+ HyteraTokenType getTokenType()
+ {
+ return HyteraTokenType.RRS_UNKNOWN;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/MessageHeader.java b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/TmsMessageHeader.java
similarity index 73%
rename from src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/MessageHeader.java
rename to src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/TmsMessageHeader.java
index 3601145c..56fd814b 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/MessageHeader.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ip/hytera/sds/TmsMessageHeader.java
@@ -22,16 +22,19 @@ package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
- * Message Header
- *
+ * Text Message Service (TMS) Message Header
+ *
* Initial token that specifies the overall message length and some other unidentified bit fields.
- *
+ *
* Note: I've only confirmed this is the start token for long SMS messages, but it may also be the start token for
* other packet data messages like GPS or any other packet overlay application.
*/
-public class MessageHeader extends HyteraToken
+public class TmsMessageHeader extends HyteraToken
{
- private static int[] MESSAGE_LENGTH = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+ private static int[] MESSAGE_LENGTH_BCD_THOUSANDS = new int[]{16, 17, 18, 19};
+ private static int[] MESSAGE_LENGTH_BCD_HUNDREDS = new int[]{20, 21, 22, 23};
+ private static int[] MESSAGE_LENGTH_BCD_TENS = new int[]{24, 25, 26, 27};
+ private static int[] MESSAGE_LENGTH_BCD_ONES = new int[]{28, 29, 30, 31};
private static int[] MESSAGE_FLAGS = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
/**
@@ -39,7 +42,7 @@ public class MessageHeader extends HyteraToken
*
* @param message containing both the token, run-length, and content
*/
- public MessageHeader(CorrectedBinaryMessage message)
+ public TmsMessageHeader(CorrectedBinaryMessage message)
{
super(message);
}
@@ -56,7 +59,7 @@ public class MessageHeader extends HyteraToken
@Override
HyteraTokenType getTokenType()
{
- return HyteraTokenType.MESSAGE_HEADER;
+ return HyteraTokenType.TEXT_MESSAGE_SERVICE_HEADER;
}
/**
@@ -65,6 +68,7 @@ public class MessageHeader extends HyteraToken
* - Priority/Flash (boolean)
* - Encryption Type (enum of at least 4 values, ie 2+ bits)
* - Emergency (boolean)
+ *
* @return
*/
public String getFlags()
@@ -78,6 +82,10 @@ public class MessageHeader extends HyteraToken
*/
public int getMessageLength()
{
- return mMessage.getInt(MESSAGE_LENGTH);
+ int length = mMessage.getInt(MESSAGE_LENGTH_BCD_THOUSANDS) * 1000;
+ length += mMessage.getInt(MESSAGE_LENGTH_BCD_HUNDREDS) * 100;
+ length += mMessage.getInt(MESSAGE_LENGTH_BCD_TENS) * 10;
+ length += mMessage.getInt(MESSAGE_LENGTH_BCD_ONES);
+ return length;
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/DMRIpAddress.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/DMRIpAddress.java
new file mode 100644
index 00000000..5f9ba72a
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/ipv4/DMRIpAddress.java
@@ -0,0 +1,51 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 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.identifier.ipv4;
+
+import io.github.dsheirer.identifier.Role;
+import io.github.dsheirer.identifier.ipv4.IPV4Address;
+import io.github.dsheirer.identifier.ipv4.IPV4Identifier;
+import io.github.dsheirer.protocol.Protocol;
+
+/**
+ * DMR IPV4 Address
+ */
+public class DMRIpAddress extends IPV4Identifier
+{
+ public DMRIpAddress(IPV4Address ipAddress, Role role)
+ {
+ super(ipAddress, role);
+ }
+
+ public Protocol getProtocol()
+ {
+ return Protocol.DMR;
+ }
+
+ public static DMRIpAddress createTo(int value)
+ {
+ return new DMRIpAddress(new IPV4Address(value), Role.TO);
+ }
+
+ public static DMRIpAddress createFrom(int value)
+ {
+ return new DMRIpAddress(new IPV4Address(value), Role.FROM);
+ }
+}