#2175 DMR Text Message Service (TMS) support. (#2176)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2025-03-10 03:45:16 -04:00
committed by GitHub
parent 1cb0490985
commit dec255d444
5 changed files with 205 additions and 3 deletions

View File

@@ -101,6 +101,7 @@ import io.github.dsheirer.module.decode.ip.hytera.shortdata.HyteraShortDataPacke
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.tms.TMSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.source.tuner.channel.rotation.AddChannelRotationActiveStateRequest;
@@ -503,6 +504,19 @@ public class DMRDecoderState extends TimeslotDecoderState
.build();
broadcast(shortDataEvent);
}
//Motorola TMS
else if(packet.getPacket() instanceof TMSPacket tms)
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
DecodeEvent shortDataEvent = DMRDecodeEvent.builder(DecodeEventType.TEXT_MESSAGE, packet.getTimestamp())
.channel(getCurrentChannel())
.identifiers(mic)
.timeslot(getTimeslot())
.details(tms.toString())
.build();
broadcast(shortDataEvent);
}
//Motorola XCMP
else if(packet.getPacket() instanceof XCMPPacket xcmp)
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -78,6 +78,7 @@ public enum DecodeEventType
SMS("SMS"),
STATION_ID("Station ID"),
STATUS("Status"),
TEXT_MESSAGE("Text Message"),
UDP_PACKET("UDP/IP Packet"),
UNKNOWN_PACKET("Unknown Packet"),
XCMP("Motorola XCMP"),

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -27,6 +27,7 @@ import io.github.dsheirer.module.decode.ip.ipv4.IPV4Header;
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.tms.TMSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
import io.github.dsheirer.module.decode.ip.udp.UDPPort;
@@ -132,7 +133,7 @@ public class PacketMessageFactory
case 4005: //Automatic Registration Service
return new ARSPacket(binaryMessage, offset);
case 4007: //Text Message Service
break;
return new TMSPacket(binaryMessage, offset);
case 4008: //Telemetry Service
break;
case 4009: //Over The Air Programming (OTAP)

View File

@@ -0,0 +1,84 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.ip.mototrbo.tms;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.ip.Header;
import java.util.Collections;
import java.util.List;
/**
* Text Messaging Service (TMS) Header
*
* The payload contains 4x bytes of ?UTF-8? values (0-9) indicating the message length followed by UTF-16 Little Endian
* characters.
*/
public class TMSHeader extends Header
{
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param message containing the header
* @param offset to the header within the message
*/
public TMSHeader(BinaryMessage message, int offset)
{
super(message, offset);
}
/**
* Character count is stored in the first 4x bytes as UTF-8 values.
*/
public int getCharacterCount()
{
StringBuilder sb = new StringBuilder();
sb.append((char)getMessage().getByte(getOffset() + 0));
sb.append((char)getMessage().getByte(getOffset() + 8));
sb.append((char)getMessage().getByte(getOffset() + 16));
sb.append((char)getMessage().getByte(getOffset() + 24));
try
{
return Integer.parseInt(sb.toString());
}
catch(Exception e)
{
return 0;
}
}
/**
* Length of this header in bits
*/
@Override
public int getLength()
{
return 32;
}
/**
* Identifiers for this packet
*/
public List<Identifier> getIdentifiers()
{
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,102 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2025 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.ip.mototrbo.tms;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.ip.IPacket;
import io.github.dsheirer.module.decode.ip.Packet;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
/**
* Text Messaging Service (TMS) message packet.
*/
public class TMSPacket extends Packet
{
private TMSHeader mHeader;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param message containing the packet
* @param offset to the packet within the message
*/
public TMSPacket(CorrectedBinaryMessage message, int offset)
{
super(message, offset);
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("TEXT MESSAGE: ").append(getTextMessage());
return sb.toString();
}
/**
* Extracts the text message payload.
*/
public String getTextMessage()
{
int byteCount = (getMessage().length() - 32) / 8;
if(byteCount > 0)
{
byte[] message = new byte[byteCount];
for(int x = 0; x < byteCount; x++)
{
message[x] = getMessage().getByte(getOffset() + 32 + (x * 8));
}
return new String(message, StandardCharsets.UTF_16LE);
}
return "(ERROR-INSUFFICIENT BYTES)";
}
@Override
public TMSHeader getHeader()
{
if(mHeader == null)
{
mHeader = new TMSHeader(getMessage(), getOffset());
}
return mHeader;
}
@Override
public IPacket getPayload()
{
//There is no child payload.
return null;
}
@Override
public List<Identifier> getIdentifiers()
{
//Massage and Header don't have any additional identifiers.
return Collections.emptyList();
}
}