Compare commits

..

5 Commits

134 changed files with 2836 additions and 843 deletions

View File

@@ -2,7 +2,7 @@ import java.text.SimpleDateFormat
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -18,6 +18,29 @@ import java.text.SimpleDateFormat
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
/**
* Instructions for building/compiling the sdrtrunk application.
*
* Prerequisites:
* Install and configure an OpenJDK version 19+ that includes the JavaFX modules (e.g. Bellsoft Liberica JDK)
* - Optional: install and configure Gradle 7.6+
*
* Scenario 1: run the application via gradle command line from the source code root directory:
* command: ./gradlew run
*
* Scenario 2: create an application release for the current operating system
* command: ./gradlew runtimeZipCurrent
* Note: release image is located in the /build/image/ directory
*
* Scenario 3: create releases for Linux and OSX operating systems using downloaded JDKs
* command: ./gradlew runtimeZipOthers
* Note: release images are located in the /build/image/ directory
*
* Scenario 4: create release for Windows operating system using downloaded JDK
* command: ./gradlew runtimeZipWindows
* Note: release image is located in the /build/image/ directory
*/
plugins {
id 'application'
id 'java'
@@ -30,7 +53,7 @@ repositories {
maven { url "https://jitpack.io" }
}
version = '0.6.0-alpha2'
version = '0.6.0-alpha4'
//Java 19 is required for this version of the Project Panama preview/incubator feature
java {
@@ -98,6 +121,8 @@ dependencies {
implementation 'pl.edu.icm:JLargeArrays:1.6'
}
def os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem
/**
* This is needed for the JDK19 panama and vector API ... until it moves out of incubation or preview
*/
@@ -106,24 +131,35 @@ tasks.withType(JavaCompile) {
options.compilerArgs.add("--add-modules=jdk.incubator.vector") //Needed while Panama Vector API remains in incubator
}
application {
mainClassName = "io.github.dsheirer.gui.SDRTrunk"
applicationDefaultJvmArgs =
/**
* JVM Arguments for running the application on Windows.
*/
def jvmArgsWindows =
['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED', //Needed for controls-fx.jar
'--add-exports=java.desktop/com.sun.java.swing.plaf.windows=ALL-UNNAMED', //windows users for JIDE
'--add-modules=jdk.incubator.vector', //Needed while Project Panama API is still in incubator
'--enable-preview', //Project Panama Vector & Foreign Function APIs remain in incubator
'--enable-native-access=ALL-UNNAMED',
'-Djava.library.path=c:\\Program Files\\SDRplay\\API\\x64'] //Allows Project Panama Foreign Function access to system library
/**
* JVM Arguments for running the application on Linux.
*/
def jvmArgsLinux =
['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED', //Needed for controls-fx.jar
'--add-modules=jdk.incubator.vector', //Needed while Project Panama API is still in incubator
'--enable-preview', //Project Panama Vector & Foreign Function APIs remain in incubator
'--enable-native-access=ALL-UNNAMED'] //Allows Project Panama Foreign Function access to system library
'--enable-native-access=ALL-UNNAMED',
'-Djava.library.path=/usr/local/lib'] //Allows Project Panama Foreign Function access to system library
application {
mainClassName = "io.github.dsheirer.gui.SDRTrunk"
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
if(os.isWindows()) {
//Replace the default Java system library path with an explicit location for the SDRplay API library (Windows)
applicationDefaultJvmArgs.add('-Djava.library.path=c:\\Program Files\\SDRplay\\API\\x64')
//On windows, we also need to export the windows swing look & feel module
applicationDefaultJvmArgs.add('--add-exports=java.desktop/com.sun.java.swing.plaf.windows=ALL-UNNAMED')
applicationDefaultJvmArgs = jvmArgsWindows
}
else {
//Replace the default Java system library path with an explicit location for the SDRplay API library (Linux/OS-X)
applicationDefaultJvmArgs.add("-Djava.library.path=/usr/local/lib")
applicationDefaultJvmArgs = jvmArgsLinux
}
}
@@ -167,85 +203,152 @@ String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-19.0.1-full.jdk'
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-19.0.1-full.jdk'
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-19.0.1-full'
boolean hasTargetJdks = file(jdk_linux_x86_64).exists() || file(jdk_linux_aarch64).exists() ||
file(jdk_osx_x86_64).exists() || file(jdk_osx_aarch64).exists() || file(jdk_windows_x86_64).exists()
/**
* Download URLs to download the JDK as part of the gradle build packaging process
*/
def jdk_download_base = "https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-"
def jdk_download_suffix = "-full.tar.gz"
def jdk_download_linux_aarch64 = jdk_download_base + "linux-aarch64" + jdk_download_suffix
def jdk_download_linux_x86_64 = jdk_download_base + "linux-amd64" + jdk_download_suffix
def jdk_download_osx_x86_64 = jdk_download_base + "macos-aarch64" + jdk_download_suffix
def jdk_download_osx_aarch64 = jdk_download_base + "macos-amd64" + jdk_download_suffix
def jdk_download_windows_x86_64 = jdk_download_base + "windows-amd64-full.zip"
/**
* Auto-download target JDKs. Defaults to false, or the user can override via the gradle command line by
* including this option: '-PdownloadJdks=true'
* Configures the runtime zip task with additional options/settings.
* @param rt task to configure
* @param jvmArgs to use (windows or linux)
* @return configured task.
*/
boolean downloadJdks = project.hasProperty('downloadJdks') ? project.getProperty('downloadJdks') : false
/**
* Beryx Runtime plugin for creating OS and CPU targeted build(s) of the software.
*
* Option 1: Create a single target build using the locally installed JDK, or optionally create target builds for
* all OS and CPU targets when pre-downloaded target JDKs are available (see 'jdk_*' properties above).
*
* >: gradle runtimeZip
*
* Option 2: Create all OS and CPU target builds by automatically downloading all JDKs needed to build the target images
*
* >: gradle runtimeZip -PdownloadJdks=true
*/
runtime {
//Build by automatically downloading JDKs for the OS and CPU targets
if(downloadJdks) {
println("Building all runtime images")
println("The [ > jre: ] build phase downloads several target JDKs and this may take a while ... please wait")
targetPlatform(targetLinuxAarch64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-linux-aarch64-full.tar.gz")
}
targetPlatform(targetLinuxX86_64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-linux-amd64-full.tar.gz")
}
targetPlatform(targetOsxAarch64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-macos-aarch64-full.tar.gz")
}
targetPlatform(targetOsxX86_64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-macos-amd64-full.tar.gz")
}
targetPlatform(targetWindowsX86_64) {
jdkHome = jdkDownload("https://download.bell-sw.com/java/19.0.1+11/bellsoft-jdk19.0.1+11-windows-amd64-full.zip")
}
}
//Build using pre-downloaded JDKs
else if(hasTargetJdks) {
println("Building all runtime images using pre-downloaded JDKs")
if(file(jdk_linux_aarch64).exists()) {
targetPlatform(targetLinuxAarch64, jdk_linux_aarch64)
}
if(file(jdk_linux_x86_64).exists()) {
targetPlatform(targetLinuxX86_64, jdk_linux_x86_64)
}
if(file(jdk_osx_aarch64).exists()) {
targetPlatform(targetOsxAarch64, jdk_osx_aarch64)
}
if(file(jdk_osx_x86_64).exists()) {
targetPlatform(targetOsxX86_64, jdk_osx_x86_64)
}
if(file(jdk_windows_x86_64).exists()) {
targetPlatform(targetWindowsX86_64, jdk_windows_x86_64)
}
}
//Build using locally installed JDK only
else {
println("Building a single runtime image using locally installed JDK")
}
def configure(org.beryx.runtime.RuntimeZipTask rt, List<String> jvmArgs) {
//Use the correct set of JVM arguments
rt.extension.launcherData.get().jvmArgs = jvmArgs
//Additional modules to include with auto-detected modules.
//jdk.crypto.ec - needed for HTTPS connections (broadcastify calls & map tile server)
//jdk.incubator.vector - needed for Project Panama foreign function and vector apis
//jdk.accessibility is used with assistive technologies like screen readers
modules = ['jdk.crypto.ec','jdk.incubator.vector','jdk.accessibility']
rt.extension.addModules('jdk.crypto.ec', 'jdk.incubator.vector', 'jdk.accessibility')
//Use auto-detected modules and 'add' any specified modules.
additive = true
rt.extension.additive.set(true)
//Recommended options for reducing/shrinking runtime target image size
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
rt.extension.addOptions('--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages')
//Runtime images have auto appended version in image zip name. Override to include version in single target image.
imageZip = (hasTargetJdks | downloadJdks) ? file("$buildDir/image/sdr-trunk.zip") :
file("$buildDir/image/sdr-trunk-" + version + ".zip")
rt.extension.imageZip.set(file("$buildDir/image/sdr-trunk.zip"))
}
/**
* Creates a release package for the current operating system.
*
* Note: you cannot create both Windows and Linux releases at the same time due to limitations of the Runtime plugin
*
* Usage: gradle runtimeZipCurrent
* Usage: gradlew runtimeZipCurrent (windows - using gradlew)
* Usage: ./gradlew runtimeZipCurrent (linux - using gradlew)
*/
tasks.register('runtimeZipCurrent', org.beryx.runtime.RuntimeZipTask) {rt ->
def arch = System.properties.getProperty("os.arch")
def javaHome = org.gradle.internal.jvm.Jvm.current().getJavaHome().toString()
if(os.isWindows()) {
rt.extension.targetPlatform(targetWindowsX86_64, javaHome)
configure(rt, jvmArgsWindows)
}
else if(os.isLinux())
{
if(arch.equals("amd64")) {
rt.extension.targetPlatform(targetLinuxX86_64, javaHome)
}
else if(arch.equals("aarch64")) {
rt.extension.targetPlatform(targetLinuxAarch64, javaHome)
}
configure(rt, jvmArgsLinux)
}
else if(os.isMacOsX())
{
if(arch.equals("amd64")) {
rt.extension.targetPlatform(targetOsxX86_64, javaHome)
}
else if(arch.equals("aarch64")) {
rt.extension.targetPlatform(targetOsxAarch64, javaHome)
}
configure(rt, jvmArgsLinux)
}
}
/**
* Creates a Windows release package.
*
* Note: you cannot create both Windows and Linux releases at the same time due to limitations of the Runtime plugin
*
* Usage: gradle runtimeZipWindows
* Usage: gradlew runtimeZipWindows (windows - using gradlew)
* Usage: ./gradlew runtimeZipWindows (linux - using gradlew)
*/
tasks.register('runtimeZipWindows', org.beryx.runtime.RuntimeZipTask) {rt ->
if(file(jdk_windows_x86_64).exists()) {
rt.extension.targetPlatform(targetWindowsX86_64, jdk_windows_x86_64, [])
}
else {
rt.extension.targetPlatform(targetWindowsX86_64) {
jdkHome = jdkDownload(jdk_download_windows_x86_64)
}
}
configure(rt, jvmArgsWindows)
}
/**
* Creates Linux & OSX release packages.
*
* Note: you cannot create both Windows and Linux releases at the same time due to limitations of the Runtime plugin
*
* Usage: gradle runtimeZipOthers
* Usage: gradlew runtimeZipOthers (windows - using gradlew)
* Usage: ./gradlew runtimeZipOthers (linux - using gradlew)
*/
tasks.register('runtimeZipOthers', org.beryx.runtime.RuntimeZipTask) {rt ->
//Linux aarch64
if(file(jdk_linux_aarch64).exists()) {
rt.extension.targetPlatform(targetLinuxAarch64, jdk_linux_aarch64, [])
}
else {
rt.extension.targetPlatform(targetLinuxAarch64) {
jdkHome = jdkDownload(jdk_download_linux_aarch64)
}
}
//Linux x86-64
if(file(jdk_linux_x86_64).exists()) {
rt.extension.targetPlatform(targetLinuxX86_64, jdk_linux_x86_64, [])
}
else {
rt.extension.targetPlatform(targetLinuxX86_64) {
jdkHome = jdkDownload(jdk_download_linux_x86_64)
}
}
//OSX aarch64
if(file(jdk_osx_aarch64).exists()) {
rt.extension.targetPlatform(targetOsxAarch64, jdk_osx_aarch64, [])
}
else {
rt.extension.targetPlatform(targetOsxAarch64) {
jdkHome = jdkDownload(jdk_download_osx_aarch64)
}
}
//Linux x86-64
if(file(jdk_osx_x86_64).exists()) {
rt.extension.targetPlatform(targetOsxX86_64, jdk_osx_x86_64, [])
}
else {
rt.extension.targetPlatform(targetOsxX86_64) {
jdkHome = jdkDownload(jdk_download_osx_x86_64)
}
}
configure(rt, jvmArgsLinux);
}

View File

@@ -33,6 +33,8 @@ public class BinaryMessage extends BitSet
private static final int[] CHARACTER_7_BIT = new int[]{0, 1, 2, 3, 4, 5, 6};
private static final int[] CHARACTER_8_BIT = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
private static final String UTF_8 = "UTF-8";
private static final String GB2312 = "GB2312";
/**
* Logical (ie constructed) size of this bitset, despite the actual size of
@@ -1236,6 +1238,26 @@ public class BinaryMessage extends BitSet
return new String(bytes, UTF_8);
}
/**
* Parse GB2312 Simplified Chinese Characters from the message starting at the offset.
* @param offset where to start parsing.
* @param characterCount number of characters to parse
* @return parsed message.
* @throws UnsupportedEncodingException if GB2312 encoding is not supported.
*/
public String parseGB2312(int offset, int characterCount) throws UnsupportedEncodingException
{
int length = characterCount * 2;
byte[] bytes = new byte[length];
for(int x = 0; x < length; x++)
{
bytes[x] = getByte(CHARACTER_8_BIT, x * 8 + offset);
}
return new String(bytes, GB2312);
}
/**
* Parse 16-bit unicode characters from the message starting at the offset.
* @param offset where to start parsing.

View File

@@ -28,11 +28,12 @@ import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.tuner.channel.StreamProcessorWithHeartbeat;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Polyphase Channelizer's Tuner Channel Source implementation. Wraps a ChannelOutputProcessor instance and
@@ -201,6 +202,7 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
if(mPolyphaseChannelOutputProcessor != null)
{
mPolyphaseChannelOutputProcessor.setListener(null);
mPolyphaseChannelOutputProcessor.stop();
}
mPolyphaseChannelOutputProcessor = null;

View File

@@ -26,6 +26,8 @@ import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
import io.github.dsheirer.record.binary.BinaryReader;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.prefs.Preferences;
import javafx.application.Application;
@@ -35,6 +37,7 @@ import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
@@ -42,9 +45,15 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
@@ -60,6 +69,7 @@ import org.slf4j.LoggerFactory;
public class DMRRecordingViewer extends VBox
{
private static final Logger mLog = LoggerFactory.getLogger(DMRRecordingViewer.class);
private static final KeyCodeCombination KEY_CODE_COPY = new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_ANY);
private static final String LAST_SELECTED_DIRECTORY = "last.selected.directory";
private Preferences mPreferences = Preferences.userNodeForPackage(DMRRecordingViewer.class);
private Scene mScene;
@@ -243,7 +253,56 @@ public class DMRRecordingViewer extends VBox
{
if(mMessageTableView == null)
{
mMessageTableView = new TableView<>(mFilteredMessages);
mMessageTableView = new TableView<>();
SortedList<IMessage> sortedList = new SortedList<>(mFilteredMessages);
sortedList.comparatorProperty().bind(mMessageTableView.comparatorProperty());
mMessageTableView.setItems(sortedList);
mMessageTableView.setOnKeyPressed(event ->
{
if(KEY_CODE_COPY.match(event))
{
final Set<Integer> rows = new TreeSet<>();
for (final TablePosition tablePosition : mMessageTableView.getSelectionModel().getSelectedCells())
{
rows.add(tablePosition.getRow());
}
final StringBuilder sb = new StringBuilder();
boolean firstRow = true;
for (final Integer row : rows)
{
if(firstRow)
{
firstRow = false;
}
else
{
sb.append('\n');
}
boolean firstCol = true;
for (final TableColumn<?, ?> column : mMessageTableView.getColumns())
{
if(firstCol)
{
firstCol = false;
}
else
{
sb.append('\t');
}
final Object cellData = column.getCellData(row);
sb.append(cellData == null ? "" : cellData.toString());
}
}
final ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(sb.toString());
Clipboard.getSystemClipboard().setContent(clipboardContent);
}
});
TableColumn timestampColumn = new TableColumn();
timestampColumn.setPrefWidth(110);

View File

@@ -27,6 +27,7 @@ import io.github.dsheirer.channel.state.TimeslotDecoderState;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.Channel.ChannelType;
import io.github.dsheirer.controller.channel.ChannelConfigurationChangeNotification;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.IdentifierCollection;
@@ -74,6 +75,7 @@ import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.source.tuner.channel.rotation.AddChannelRotationActiveStateRequest;
import io.github.dsheirer.util.PacketUtil;
@@ -299,27 +301,47 @@ public class DMRDecoderState extends TimeslotDecoderState
{
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.DATA_PACKET.name())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.timeslot(packet.getTimeslot())
.details(packet.toString())
.build();
//Hytera SDS Long SMS message
if(packet.getPacket() instanceof HyteraSmsPacket hyteraSmsPacket)
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
mic.remove(Form.RADIO);
mic.remove(Form.TALKGROUP);
mic.update(hyteraSmsPacket.getSource());
mic.update(hyteraSmsPacket.getDestination());
broadcast(packetEvent);
GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket());
if (geoPosition != null) {
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.SMS.name())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("SMS:" + hyteraSmsPacket.getSMS())
.build();
broadcast(smsEvent);
}
else
{
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.DATA_PACKET.name())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.protocol(Protocol.LRRP)
.location(geoPosition)
.timeslot(packet.getTimeslot())
.details(packet.toString())
.build();
broadcast(plottableDecodeEvent);
broadcast(packetEvent);
GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket());
if (geoPosition != null) {
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.protocol(Protocol.LRRP)
.location(geoPosition)
.build();
broadcast(plottableDecodeEvent);
}
}
}
@@ -628,7 +650,7 @@ public class DMRDecoderState extends TimeslotDecoderState
break;
case HYTERA_08_ANNOUNCEMENT:
case HYTERA_68_ANNOUNCEMENT:
case HYTERA_XPT_SITE_STATE:
case HYTERA_68_XPT_SITE_STATE:
case MOTOROLA_CAPPLUS_NEIGHBOR_REPORT:
if(csbk instanceof CapacityPlusNeighbors)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -30,17 +30,14 @@ import io.github.dsheirer.module.decode.dmr.message.DMRBurst;
import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
import io.github.dsheirer.module.decode.dmr.message.DMRMessageFactory;
import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.lrrp.token.Identity;
import io.github.dsheirer.module.decode.ip.lrrp.token.Token;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token.Identity;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token.Token;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.record.binary.BinaryReader;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.DirectoryStream;
@@ -53,6 +50,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DMR Sync Detector and Message Framer. Includes capability to detect PLL out-of-phase lock errors

View File

@@ -0,0 +1,78 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.identifier;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.message.type.Tier3Gateway;
/**
* DMR Tier-III (ie trunked) radio identifier with custom overrides to correctly label Gateways & Identities
*/
public class DmrTier3Radio extends DMRRadio
{
public DmrTier3Radio(Integer value, Role role)
{
super(value, role);
}
public boolean isGateway()
{
return Tier3Gateway.isGateway(getValue());
}
@Override
public String toString()
{
if(isGateway())
{
return Tier3Gateway.fromValue(getValue()).getLabel();
}
else
{
return super.toString();
}
}
/**
* Creates a DMR TO radio identifier
*/
public static RadioIdentifier createTo(int radioId)
{
return new DmrTier3Radio(radioId, Role.TO);
}
/**
* Creates a DMR FROM radio identifier
*/
public static RadioIdentifier createFrom(int radioId)
{
return new DmrTier3Radio(radioId, Role.FROM);
}
/**
* Creates a DMR ANY radio identifier
*/
public static RadioIdentifier createAny(int radioId)
{
return new DmrTier3Radio(radioId, Role.ANY);
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -25,7 +25,6 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import java.util.Collections;
import java.util.List;
@@ -34,6 +33,8 @@ import java.util.List;
*/
public abstract class DataBlock extends DataMessage
{
private static final int[] DATA_BLOCK_SERIAL_NUMBER = new int[]{0, 1, 2, 3, 4, 5, 6};
/**
* Constructs an instance.
*
@@ -63,7 +64,11 @@ public abstract class DataBlock extends DataMessage
/**
* Serial number for a confirmed data block
*/
public abstract int getDataBlockSerialNumber();
public int getDataBlockSerialNumber()
{
return getMessage().getInt(DATA_BLOCK_SERIAL_NUMBER);
}
@Override
public String toString()

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
*/
public class DataBlock1Rate extends DataBlock
{
private static final int[] DATA_BLOCK_SERIAL_NUMBER = new int[]{0, 1, 2, 3, 4, 5, 6};
private static final int[] CRC = new int[]{7, 8, 9, 10, 11, 12, 13, 14, 15};
private static final int CONFIRMED_PAYLOAD_START = 16;
private static final int UNCONFIRMED_PAYLOAD_START = 0;
@@ -56,17 +55,16 @@ public class DataBlock1Rate extends DataBlock
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("RATE 1/1 DATA CONFIRMED:").append(getConfirmedPayload().toHexString());
sb.append("CC:").append(getSlotType().getColorCode());
if(!isValid())
{
sb.append(" [CRC ERROR]");
}
sb.append(" RATE 1/1 DATA CONFIRMED:").append(getConfirmedPayload().toHexString());
sb.append(" UNCONFIRMED:").append(getUnConfirmedPayload().toHexString());
return sb.toString();
}
@Override
public int getDataBlockSerialNumber()
{
return getMessage().getInt(DATA_BLOCK_SERIAL_NUMBER);
}
@Override
public CorrectedBinaryMessage getConfirmedPayload()
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
*/
public class DataBlock1_2Rate extends DataBlock
{
private static final int[] DATA_BLOCK_SERIAL_NUMBER = new int[]{0, 1, 2, 3, 4, 5, 6};
private static final int[] CRC = new int[]{7, 8, 9, 10, 11, 12, 13, 14, 15};
private static final int CONFIRMED_PAYLOAD_START = 16;
private static final int UNCONFIRMED_PAYLOAD_START = 0;
@@ -56,17 +55,16 @@ public class DataBlock1_2Rate extends DataBlock
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("RATE 1/2 DATA CONFIRMED:").append(getConfirmedPayload().toHexString());
sb.append("CC:").append(getSlotType().getColorCode());
if(!isValid())
{
sb.append(" [CRC ERROR]");
}
sb.append(" RATE 1/2 DATA CONFIRMED:").append(getConfirmedPayload().toHexString());
sb.append(" UNCONFIRMED:").append(getUnConfirmedPayload().toHexString());
return sb.toString();
}
@Override
public int getDataBlockSerialNumber()
{
return getMessage().getInt(DATA_BLOCK_SERIAL_NUMBER);
}
@Override
public CorrectedBinaryMessage getConfirmedPayload()
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
*/
public class DataBlock3_4Rate extends DataBlock
{
private static final int[] DATA_BLOCK_SERIAL_NUMBER = new int[]{0, 1, 2, 3, 4, 5, 6};
private static final int[] CRC = new int[]{7, 8, 9, 10, 11, 12, 13, 14, 15};
private static final int CONFIRMED_PAYLOAD_START = 16;
private static final int UNCONFIRMED_PAYLOAD_START = 0;
@@ -57,17 +56,16 @@ public class DataBlock3_4Rate extends DataBlock
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("RATE 3/4 DATA CONFIRMED:").append(getConfirmedPayload().toHexString());
sb.append("CC:").append(getSlotType().getColorCode());
if(!isValid())
{
sb.append(" [CRC ERROR]");
}
sb.append(" RATE 3/4 DATA CONFIRMED:").append(getConfirmedPayload().toHexString());
sb.append(" UNCONFIRMED:").append(getUnConfirmedPayload().toHexString());
return sb.toString();
}
@Override
public int getDataBlockSerialNumber()
{
return getMessage().getInt(DATA_BLOCK_SERIAL_NUMBER);
}
@Override
public CorrectedBinaryMessage getConfirmedPayload()
{

View File

@@ -23,13 +23,14 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.Hytera08Acknowledge;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.Hytera68Acknowledge;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraAdjacentSiteInformation;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraAloha;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraAnnouncement;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko32;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko44;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraCsbko47;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraSmsWaitingNotification;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraSmsAvailableNotification;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTPreamble;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraXPTSiteState;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAloha;
@@ -221,7 +222,7 @@ public class CSBKMessageFactory
csbk = new Preamble(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_ALOHA:
case HYTERA_68_ALOHA:
csbk = new HyteraAloha(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_08_ANNOUNCEMENT:
@@ -237,23 +238,26 @@ public class CSBKMessageFactory
break;
}
break;
case HYTERA_XPT_PREAMBLE:
case HYTERA_68_XPT_PREAMBLE:
csbk = new HyteraXPTPreamble(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_XPT_SITE_STATE:
case HYTERA_68_XPT_SITE_STATE:
csbk = new HyteraXPTSiteState(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_CSBKO_32:
csbk = new HyteraCsbko32(pattern, message, cach, slotType, timestamp, timeslot);
case HYTERA_08_ACKNOWLEDGE:
csbk = new Hytera08Acknowledge(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_CSBKO_44:
case HYTERA_08_CSBKO_44:
csbk = new HyteraCsbko44(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_CSBKO_47:
case HYTERA_08_CSBKO_47:
csbk = new HyteraCsbko47(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_CSBKO_62:
csbk = new HyteraSmsWaitingNotification(pattern, message, cach, slotType, timestamp, timeslot);
case HYTERA_68_ACKNOWLEDGE:
csbk = new Hytera68Acknowledge(pattern, message, cach, slotType, timestamp, timeslot);
break;
case HYTERA_68_CSBKO_62:
csbk = new HyteraSmsAvailableNotification(pattern, message, cach, slotType, timestamp, timeslot);
break;
case MOTOROLA_CAPMAX_ALOHA:
csbk = new CapacityMaxAloha(pattern, message, cach, slotType, timestamp, timeslot);

View File

@@ -89,16 +89,18 @@ public enum Opcode
MOTOROLA_CAPPLUS_PREAMBLE(Vendor.MOTOROLA_CAPACITY_PLUS, 61, "PREAMBLE"),
MOTOROLA_CAPPLUS_SYSTEM_STATUS(Vendor.MOTOROLA_CAPACITY_PLUS, 62, "SYSTEM STATUS"),
HYTERA_CSBKO_32(Vendor.HYTERA_8, 32, "T-III HYTERA 08 CSBKO 32"),
HYTERA_08_ANNOUNCEMENT(Vendor.HYTERA_8, 40, "T-III 08 ANNOUNCEMENT"),
HYTERA_CSBKO_44(Vendor.HYTERA_8, 44, "T-III HYTERA 08 CSBKO 44"),
HYTERA_CSBKO_47(Vendor.HYTERA_8, 47, "T-III HYTERA 08 CSBKO 47"),
HYTERA_08_ACKNOWLEDGE(Vendor.HYTERA_8, 32, "HYTERA 08 ACKNOWLEDGE"),
HYTERA_08_ANNOUNCEMENT(Vendor.HYTERA_8, 40, "HYTERA 08 ANNOUNCEMENT"),
HYTERA_08_CSBKO_44(Vendor.HYTERA_8, 44, "HYTERA 08 CSBKO 44"),
HYTERA_08_CSBKO_47(Vendor.HYTERA_8, 47, "HYTERA 08 CSBKO 47"),
HYTERA_XPT_SITE_STATE(Vendor.HYTERA_68, 10, "HYTERA XPT SITE STATE"),
HYTERA_ALOHA(Vendor.HYTERA_68, 25, "HYTERA ALOHA"),
HYTERA_68_ANNOUNCEMENT(Vendor.HYTERA_68, 40, "T-III 68 ANNOUNCEMENT"),
HYTERA_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA XPT PREAMBLE"), //Opcode 54 and/or 55 - See patent on Hytera call forwarding
HYTERA_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA CSBKO 62"),
HYTERA_68_XPT_SITE_STATE(Vendor.HYTERA_68, 10, "HYTERA 68 XPT SITE STATE"),
HYTERA_68_ALOHA(Vendor.HYTERA_68, 25, "HYTERA 68 ALOHA"),
HYTERA_68_ACKNOWLEDGE(Vendor.HYTERA_68, 32, "HYTERA 68 ACKNOWLEDGE"),
HYTERA_68_ANNOUNCEMENT(Vendor.HYTERA_68, 40, "HYTERA 68 ANNOUNCEMENT"),
//Opcode 54 and/or 55 - See patent on Hytera call forwarding
HYTERA_68_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA 68 XPT PREAMBLE"),
HYTERA_68_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA 68 CSBKO 62"),
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");

View File

@@ -0,0 +1,196 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.AcknowledgeType;
import io.github.dsheirer.module.decode.dmr.message.type.Reason;
import java.util.ArrayList;
import java.util.List;
/**
* Hytera DMR Tier III - CSBKO 32 - Possibly Acknowledge Response
*
* Analysis: ... this was transmitted on the traffic channel while the mobile was at the end of a call, in the terminator
* sequence. Since this comes from the SMS gateway 16776906, I wonder if this is a traffic channel message waiting notification?
*/
public class Hytera08Acknowledge extends CSBKMessage
{
private static final int TARGET_GROUP_INDIVIDUAL_FLAG = 16;
protected static final int[] RESPONSE_INFO = new int[]{16, 17, 18, 19, 20, 21, 22};
private static final int[] ACKNOWLEDGE_TYPE = new int[]{23, 24};
//Note: acknowledge type is the first 2 bits of the reason code - intentional overlap
private static final int[] REASON_CODE = new int[]{23, 24, 25, 26, 27, 28, 29, 30};
private static final int RESERVED = 31;
private static final int[] TARGET_ADDRESS = new int[]{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[] ADDITIONAL_INFO_SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79};
protected List<Identifier> mIdentifiers;
private IntegerIdentifier mTargetAddress;
private RadioIdentifier mSourceRadio;
/**
* Constructs an instance
*
* @param syncPattern for the CSBK
* @param message bits
* @param cach for the DMR burst
* @param slotType for this message
* @param timestamp
* @param timeslot
*/
public Hytera08Acknowledge(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
{
super(syncPattern, message, cach, slotType, timestamp, timeslot);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
if(!isValid())
{
sb.append("[CRC-ERROR] ");
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" HYTERA 08 ").append(getAcknowledgeType());
sb.append(" REASON:");
if(getReason() == Reason.UNKNOWN)
{
sb.append("UNKNOWN REASON CODE:").append(getMessage().getInt(REASON_CODE));
}
else
{
sb.append(getReason());
}
sb.append(" FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetAddress());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Indicates if the target address is a talkgroup (true) or radio/gateway (false).
*/
public boolean isTargetAddressTalkgroup()
{
return getMessage().get(TARGET_GROUP_INDIVIDUAL_FLAG);
}
/**
* Acknowledgement message type
*/
public AcknowledgeType getAcknowledgeType()
{
return AcknowledgeType.fromValue(getMessage().getInt(ACKNOWLEDGE_TYPE));
}
/**
* Reason code.
*
* Note: the reason entries are coupled with the acknowledgement type and can optionally indicate when the
* additional information field contains amplifying information.
*/
public Reason getReason()
{
return getReason(getMessage());
}
/**
* Utility method to determine the reason code for the specified message
* @param message containing a reason code field
* @return reason
*/
public static Reason getReason(CorrectedBinaryMessage message)
{
return Reason.fromValue(message.getInt(REASON_CODE));
}
public IntegerIdentifier getTargetAddress()
{
if(mTargetAddress == null)
{
if(isTargetAddressTalkgroup())
{
mTargetAddress = DMRTalkgroup.create(getMessage().getInt(TARGET_ADDRESS));
}
else
{
mTargetAddress = DmrTier3Radio.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}
return mTargetAddress;
}
public boolean hasSourceRadio()
{
return true; //TODO: delineate valid reason codes here
}
/**
* Source radio identifier
*
* Note: the source radio field is dual-purpose and contains either a source radio address or
* additional information, depending on the reason code and/or acknowledgement type. Use the
* hasSourceRadio() method to first check if this message carries a source radio identifier.
*/
public RadioIdentifier getSourceRadio()
{
if(mSourceRadio == null)
{
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(ADDITIONAL_INFO_SOURCE_ADDRESS));
}
return mSourceRadio;
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getTargetAddress());
if(hasSourceRadio())
{
mIdentifiers.add(getSourceRadio());
}
}
return mIdentifiers;
}
}

View File

@@ -0,0 +1,195 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.AcknowledgeType;
import io.github.dsheirer.module.decode.dmr.message.type.Reason;
import java.util.ArrayList;
import java.util.List;
/**
* Hytera FID68 - CSBKO 32 - Probable Acknowledge
*
* Analysis: ... this was transmitted on the Tier III control channel
*/
public class Hytera68Acknowledge extends CSBKMessage
{
private static final int TARGET_GROUP_INDIVIDUAL_FLAG = 16;
protected static final int[] RESPONSE_INFO = new int[]{16, 17, 18, 19, 20, 21, 22};
private static final int[] ACKNOWLEDGE_TYPE = new int[]{23, 24};
//Note: acknowledge type is the first 2 bits of the reason code - intentional overlap
private static final int[] REASON_CODE = new int[]{23, 24, 25, 26, 27, 28, 29, 30};
private static final int RESERVED = 31;
private static final int[] TARGET_ADDRESS = new int[]{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[] ADDITIONAL_INFO_SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79};
protected List<Identifier> mIdentifiers;
private IntegerIdentifier mTargetAddress;
private RadioIdentifier mSourceRadio;
/**
* Constructs an instance
*
* @param syncPattern for the CSBK
* @param message bits
* @param cach for the DMR burst
* @param slotType for this message
* @param timestamp
* @param timeslot
*/
public Hytera68Acknowledge(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
{
super(syncPattern, message, cach, slotType, timestamp, timeslot);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
if(!isValid())
{
sb.append("[CRC-ERROR] ");
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" HYTERA 68 ").append(getAcknowledgeType());
sb.append(" REASON:");
if(getReason() == Reason.UNKNOWN)
{
sb.append("UNKNOWN REASON CODE:").append(getMessage().getInt(REASON_CODE));
}
else
{
sb.append(getReason());
}
sb.append(" FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetAddress());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Indicates if the target address is a talkgroup (true) or radio/gateway (false).
*/
public boolean isTargetAddressTalkgroup()
{
return getMessage().get(TARGET_GROUP_INDIVIDUAL_FLAG);
}
/**
* Acknowledgement message type
*/
public AcknowledgeType getAcknowledgeType()
{
return AcknowledgeType.fromValue(getMessage().getInt(ACKNOWLEDGE_TYPE));
}
/**
* Reason code.
*
* Note: the reason entries are coupled with the acknowledgement type and can optionally indicate when the
* additional information field contains amplifying information.
*/
public Reason getReason()
{
return getReason(getMessage());
}
/**
* Utility method to determine the reason code for the specified message
* @param message containing a reason code field
* @return reason
*/
public static Reason getReason(CorrectedBinaryMessage message)
{
return Reason.fromValue(message.getInt(REASON_CODE));
}
public IntegerIdentifier getTargetAddress()
{
if(mTargetAddress == null)
{
if(isTargetAddressTalkgroup())
{
mTargetAddress = DMRTalkgroup.create(getMessage().getInt(TARGET_ADDRESS));
}
else
{
mTargetAddress = DmrTier3Radio.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}
return mTargetAddress;
}
public boolean hasSourceRadio()
{
return true; //TODO: delineate valid reason codes here
}
/**
* Source radio identifier
*
* Note: the source radio field is dual-purpose and contains either a source radio address or
* additional information, depending on the reason code and/or acknowledgement type. Use the
* hasSourceRadio() method to first check if this message carries a source radio identifier.
*/
public RadioIdentifier getSourceRadio()
{
if(mSourceRadio == null)
{
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(ADDITIONAL_INFO_SOURCE_ADDRESS));
}
return mSourceRadio;
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getTargetAddress());
if(hasSourceRadio())
{
mIdentifiers.add(getSourceRadio());
}
}
return mIdentifiers;
}
}

View File

@@ -23,7 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
@@ -155,7 +155,7 @@ public class HyteraAloha extends CSBKMessage
{
if(mRadioIdentifier == null)
{
mRadioIdentifier = DMRRadio.createTo(getMessage().getInt(RADIO));
mRadioIdentifier = DmrTier3Radio.createTo(getMessage().getInt(RADIO));
}
return mRadioIdentifier;

View File

@@ -1,137 +0,0 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
/**
* Hytera DMR Tier III - CSBKO 32 - Possibly Acknowledge Response
*
* Analysis: ... this was transmitted on the traffic channel while the mobile was at the end of a call, in the terminator
* sequence. Since this comes from the SMS gateway 16776906, I wonder if this is a traffic channel message waiting notification?
*/
public class HyteraCsbko32 extends CSBKMessage
{
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] UNKNOWN_2 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] DESTINATION_RADIO = new int[]{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[] SOURCE_RADIO = new int[]{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 List<Identifier> mIdentifiers;
private RadioIdentifier mDestinationRadio;
private RadioIdentifier mSourceRadio;
/**
* Constructs an instance
*
* @param syncPattern for the CSBK
* @param message bits
* @param cach for the DMR burst
* @param slotType for this message
* @param timestamp
* @param timeslot
*/
public HyteraCsbko32(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
{
super(syncPattern, message, cach, slotType, timestamp, timeslot);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
if(!isValid())
{
sb.append("[CRC-ERROR] ");
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" HYTERA CSBKO=32 PROBABLE ACKNOWLEDGE RESPONSE");
sb.append(" FM:").append(getSourceRadio());
sb.append(" TO:").append(getDestinationRadio());
sb.append(" UNK1:").append(getUnknown1());
sb.append(" UNK2:").append(getUnknown2());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Addressed (ie TO) radio identifier
* @return radio identifier
*/
public RadioIdentifier getDestinationRadio()
{
if(mDestinationRadio == null)
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
}
return mDestinationRadio;
}
public String getUnknown1()
{
return getMessage().getHex(UNKNOWN_1, 2);
}
public String getUnknown2()
{
return getMessage().getHex(UNKNOWN_2, 2);
}
/**
* Source radio. Should be 16,777,906 the Hytera SMS gateway.
*/
public RadioIdentifier getSourceRadio()
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceRadio;
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getDestinationRadio());
mIdentifiers.add(getSourceRadio());
}
return mIdentifiers;
}
}

View File

@@ -23,7 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
@@ -94,7 +94,7 @@ public class HyteraCsbko47 extends CSBKMessage
{
if(mDestinationRadio == null)
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_RADIO));
}
return mDestinationRadio;
@@ -117,7 +117,7 @@ public class HyteraCsbko47 extends CSBKMessage
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceRadio;

View File

@@ -23,7 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
@@ -33,18 +33,16 @@ import java.util.List;
/**
* Hytera DMR Tier III - CSBKO 62 -
*
* Analysis: this message gets sent on timeslot 2 (not the Tier III control timeslot 1) from the SMS gateway (16776906)
* and gets sent to each of the radios involved in the SMS transfer (sender/receiver). I suspect that the network tells
* each of the radios to join the control channel and prepare for the SMS message.
* Analysis: this message was observed sent on timeslot 2 (not the Tier III control timeslot 1) from the SMS gateway (16776906)
* and gets sent to each of the radios involved in the SMS transfer (sender/receiver).
*/
public class HyteraSmsWaitingNotification extends CSBKMessage
public class HyteraSmsAvailableNotification extends CSBKMessage
{
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] UNKNOWN_2 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] DESTINATION_RADIO = new int[]{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[] SOURCE_RADIO = new int[]{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 static final int[] UNKNOWN = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] DESTINATION_RADIO = new int[]{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[] SOURCE_RADIO = new int[]{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 List<Identifier> mIdentifiers;
private RadioIdentifier mDestinationRadio;
@@ -57,10 +55,11 @@ public class HyteraSmsWaitingNotification extends CSBKMessage
* @param message bits
* @param cach for the DMR burst
* @param slotType for this message
* @param timestamp
* @param timeslot
* @param timestamp of the message
* @param timeslot of the message
*/
public HyteraSmsWaitingNotification(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach, SlotType slotType, long timestamp, int timeslot)
public HyteraSmsAvailableNotification(DMRSyncPattern syncPattern, CorrectedBinaryMessage message, CACH cach,
SlotType slotType, long timestamp, int timeslot)
{
super(syncPattern, message, cach, slotType, timestamp, timeslot);
}
@@ -76,11 +75,9 @@ public class HyteraSmsWaitingNotification extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" HYTERA PREPARE FOR SMS MESSAGE FOR:").append(getDestinationRadio());
sb.append(" HYTERA SMS MESSAGE AVAILABLE FOR:").append(getDestinationRadio());
sb.append(" FM:").append(getSourceRadio());
sb.append(" UNK1:").append(getUnknown1());
sb.append(" UNK2:").append(getUnknown2());
sb.append(" UNK:").append(getUnknown());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
@@ -94,20 +91,19 @@ public class HyteraSmsWaitingNotification extends CSBKMessage
{
if(mDestinationRadio == null)
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_RADIO));
}
return mDestinationRadio;
}
public String getUnknown1()
/**
* Unknown octets.
* @return two unknown octets as hex string
*/
public String getUnknown()
{
return getMessage().getHex(UNKNOWN_1, 2);
}
public String getUnknown2()
{
return getMessage().getHex(UNKNOWN_2, 2);
return getMessage().getHex(UNKNOWN, 4);
}
/**
@@ -117,7 +113,7 @@ public class HyteraSmsWaitingNotification extends CSBKMessage
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceRadio;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -24,12 +24,11 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
import java.util.ArrayList;
import java.util.List;
@@ -153,7 +152,7 @@ public class HyteraXPTPreamble extends Preamble
}
else
{
mTargetAddress = DMRRadio.createTo(getMessage().getInt(TARGET_ADDRESS));
mTargetAddress = DmrTier3Radio.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}
@@ -167,7 +166,7 @@ public class HyteraXPTPreamble extends Preamble
{
if(mSourceAddress == null)
{
mSourceAddress = DMRRadio.createFrom(getMessage().getInt(SOURCE_ADDRESS));
mSourceAddress = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_ADDRESS));
}
return mSourceAddress;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,14 +23,13 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.ServiceFunction;
import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode;
import io.github.dsheirer.module.decode.dmr.message.type.Version;
import java.util.ArrayList;
import java.util.List;
@@ -83,7 +82,7 @@ public class CapacityMaxAloha extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CAPACITY-MAX ALOHA");
sb.append(" CSBK CAPACITY-MAX ALOHA");
if(hasRadioIdentifier())
{
@@ -153,7 +152,7 @@ public class CapacityMaxAloha extends CSBKMessage
{
if(mRadioIdentifier == null)
{
mRadioIdentifier = DMRRadio.createTo(getMessage().getInt(RADIO));
mRadioIdentifier = DmrTier3Radio.createTo(getMessage().getInt(RADIO));
}
return mRadioIdentifier;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -25,7 +25,6 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.Collections;
import java.util.List;
@@ -60,7 +59,7 @@ public class CapacityPlusCSBKO_60 extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CAP+ *UNKNOWN* CSBK:60");
sb.append(" CSBK CAP+ *UNKNOWN* CSBK:60");
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -89,7 +88,7 @@ public class CapacityPlusDataRevertWindowAnnouncement extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" MOTOROLA CAP+ ENHANCED DATA REVERT ANNOUNCEMENT");
sb.append(" CSBK CAP+ ENHANCED DATA REVERT ANNOUNCEMENT");
sb.append(" WINDOW:").append(getSuperFrame()).append(".").append(getWindow());
if(hasTargetRadio())

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +71,7 @@ public class CapacityPlusDataRevertWindowGrant extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" MOTOROLA CAP+ ENHANCED DATA REVERT GRANT TO:").append(getTargetAddress());
sb.append(" CSBK CAP+ ENHANCED DATA REVERT GRANT TO:").append(getTargetAddress());
sb.append(" TRANSMIT IN WINDOW:").append(getSuperFrame()).append(".").append(getWindow());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -30,7 +30,6 @@ import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.LCSS;
import java.util.ArrayList;
import java.util.List;
@@ -90,7 +89,7 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequ
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" MOTOROLA CAP+ SITE:").append(getSite());
sb.append(" CSBK CAP+ SITE:").append(getSite());
sb.append(" REST ").append(getRestChannel());
sb.append(" FL:").append(getLCSS());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
import java.util.ArrayList;
import java.util.List;
@@ -76,7 +75,7 @@ public class CapacityPlusPreamble extends Preamble
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" MOTOROLA CAP+").append(isCSBKPreamble() ? " CSBK" : " DATA");
sb.append(" CSBK CAP+").append(isCSBKPreamble() ? " CSBK" : " DATA");
sb.append(" PREAMBLE FM:").append(getSourceAddress());
sb.append(" TO:").append(getTargetAddress());
sb.append(" BLOCKS TO FOLLOW:").append(getBlocksToFollow());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.LCSS;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +71,7 @@ public class CapacityPlusSystemStatus extends CSBKMessage implements ITimeslotFr
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" MOTOROLA CAP+ SYSTEM STATUS ").append(getFragmentIndicator());
sb.append(" CSBK CAP+ SYSTEM STATUS ").append(getFragmentIndicator());
sb.append(" REST ").append(getRestChannel());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +71,7 @@ public class ConnectPlusCSBKO_16 extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ CSBKO:16 FM:").append(getSourceRadio());
sb.append(" CSBK CON+ CSBKO:16 FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -71,7 +70,7 @@ public class ConnectPlusDataRevertWindowAnnouncement extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ ENHANCED DATA REVERT CHANNEL ANNOUNCE WINDOW:");
sb.append(" CSBK CON+ ENHANCED DATA REVERT CHANNEL ANNOUNCE WINDOW:");
sb.append(getSuperFrame()).append(".").append(getWindow());
if(hasTargetRadio())
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +71,7 @@ public class ConnectPlusDataRevertWindowGrant extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ ENHANCED DATA REVERT GRANT TO:").append(getTargetRadio());
sb.append(" CSBK CON+ ENHANCED DATA REVERT GRANT TO:").append(getTargetRadio());
sb.append(" TRANSMIT IN WINDOW:").append(getSuperFrame()).append(".").append(getWindow());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -28,7 +28,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRSite;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -70,7 +69,7 @@ public class ConnectPlusNeighborReport extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ NEIGHBORS:");
sb.append(" CSBK CON+ NEIGHBORS:");
List<SiteIdentifier> neighbors = getNeighbors();
if(neighbors.isEmpty())

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -28,8 +28,7 @@ import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.ip.xcmp.XCMPMessageType;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPMessageType;
import java.util.ArrayList;
import java.util.List;
@@ -78,7 +77,7 @@ public class ConnectPlusOTAAnnouncement extends CSBKMessage implements ITimeslot
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ ANNOUNCE OTA ").append(getMessageType());
sb.append(" CSBK CON+ ANNOUNCE OTA ").append(getMessageType());
sb.append(" VER:").append(getMessageVersion());
sb.append(" AVAILABLE ON ").append(getDataChannel());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +71,7 @@ public class ConnectPlusRegistrationRequest extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ REGISTRATION REQUEST FM:").append(getSourceRadio());
sb.append(" CSBK CON+ REGISTRATION REQUEST FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -27,7 +27,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +71,7 @@ public class ConnectPlusRegistrationResponse extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ REGISTRATION RESPONSE FM:").append(getSourceRadio());
sb.append(" CSBK CON+ REGISTRATION RESPONSE FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -74,7 +73,7 @@ public class ConnectPlusTalkgroupAffiliation extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ AFFILIATE RADIO:").append(getRadio());
sb.append(" CSBK CON+ AFFILIATE RADIO:").append(getRadio());
sb.append(" TO TALKGROUP:").append(getTalkgroup());
sb.append(" UNK:").append(getUnknown());
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -32,7 +32,6 @@ import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -80,7 +79,7 @@ public class ConnectPlusVoiceChannelUser extends CSBKMessage implements ITimeslo
}
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" CON+ VOICE CHANNEL USER FM:").append(getRadio());
sb.append(" CSBK CON+ VOICE CHANNEL USER FM:").append(getRadio());
sb.append(" TO:").append(getTalkgroup());
sb.append(" ").append(getChannel());
sb.append(" UNK:").append(getUnknownField());

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,14 +23,13 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.ServiceFunction;
import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode;
import io.github.dsheirer.module.decode.dmr.message.type.Version;
import java.util.ArrayList;
import java.util.List;
@@ -153,7 +152,7 @@ public class Aloha extends CSBKMessage
{
if(mRadioIdentifier == null)
{
mRadioIdentifier = DMRRadio.createTo(getMessage().getInt(RADIO));
mRadioIdentifier = DmrTier3Radio.createTo(getMessage().getInt(RADIO));
}
return mRadioIdentifier;

View File

@@ -29,8 +29,8 @@ import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
@@ -141,7 +141,7 @@ public class Clear extends CSBKMessage implements ITimeslotFrequencyReceiver
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE));
}
return mSourceRadio;
@@ -157,7 +157,7 @@ public class Clear extends CSBKMessage implements ITimeslotFrequencyReceiver
}
else
{
mDestinationId = DMRRadio.createTo(getMessage().getInt(DESTINATION));
mDestinationId = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION));
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -25,14 +25,13 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
import io.github.dsheirer.module.decode.dmr.message.type.AbsoluteChannelParameters;
import io.github.dsheirer.module.decode.dmr.message.type.DataType;
import java.util.ArrayList;
import java.util.List;
@@ -178,7 +177,7 @@ public class MoveTSCC extends CSBKMessage
{
if(mRadioIdentifier == null)
{
mRadioIdentifier = DMRRadio.createTo(getMessage().getInt(RADIO));
mRadioIdentifier = DmrTier3Radio.createTo(getMessage().getInt(RADIO));
}
return mRadioIdentifier;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -24,12 +24,11 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import java.util.ArrayList;
import java.util.List;
@@ -126,7 +125,7 @@ public class Preamble extends CSBKMessage
}
else
{
mTargetAddress = DMRRadio.createTo(getMessage().getInt(TARGET_ADDRESS));
mTargetAddress = DmrTier3Radio.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}
@@ -140,7 +139,7 @@ public class Preamble extends CSBKMessage
{
if(mSourceAddress == null)
{
mSourceAddress = DMRRadio.createFrom(getMessage().getInt(SOURCE_ADDRESS));
mSourceAddress = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_ADDRESS));
}
return mSourceAddress;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -24,13 +24,12 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.ProtectKind;
import java.util.ArrayList;
import java.util.List;
@@ -104,7 +103,7 @@ public class Protect extends CSBKMessage
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE));
}
return mSourceRadio;
@@ -123,7 +122,7 @@ public class Protect extends CSBKMessage
}
else
{
mDestinationId = DMRRadio.createTo(getMessage().getInt(DESTINATION));
mDestinationId = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION));
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -24,14 +24,13 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.AcknowledgeType;
import io.github.dsheirer.module.decode.dmr.message.type.Reason;
import java.util.ArrayList;
import java.util.List;
@@ -136,7 +135,7 @@ public class Acknowledge extends CSBKMessage
}
else
{
mTargetAddress = DMRRadio.createTo(getMessage().getInt(TARGET_ADDRESS));
mTargetAddress = DmrTier3Radio.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}
@@ -159,7 +158,7 @@ public class Acknowledge extends CSBKMessage
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(ADDITIONAL_INFO_SOURCE_ADDRESS));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(ADDITIONAL_INFO_SOURCE_ADDRESS));
}
return mSourceRadio;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -22,8 +22,8 @@ package io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.ahoy;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
@@ -117,7 +117,7 @@ public abstract class Ahoy extends CSBKMessage
}
else
{
mTargetAddress = DMRRadio.createTo(getMessage().getInt(TARGET_ADDRESS));
mTargetAddress = DmrTier3Radio.createTo(getMessage().getInt(TARGET_ADDRESS));
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -26,8 +26,7 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.Gateway;
import io.github.dsheirer.module.decode.dmr.message.type.Tier3Gateway;
import java.util.ArrayList;
import java.util.List;
@@ -84,9 +83,9 @@ public class AuthenticateRegisterRadioCheck extends Ahoy
/**
* Gateway address from the multi-purpose field
*/
public Gateway getGateway()
public Tier3Gateway getGateway()
{
return Gateway.fromValue(getMultiPurposeFieldValue());
return Tier3Gateway.fromValue(getMultiPurposeFieldValue());
}
/**
@@ -108,7 +107,7 @@ public class AuthenticateRegisterRadioCheck extends Ahoy
*/
public boolean isAuthenticateCommand()
{
return getGateway() != Gateway.TSI;
return getGateway() != Tier3Gateway.TSI;
}
/**

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -26,8 +26,7 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.Gateway;
import io.github.dsheirer.module.decode.dmr.message.type.Tier3Gateway;
import java.util.ArrayList;
import java.util.List;
@@ -80,9 +79,9 @@ public class StunReviveKill extends Ahoy
/**
* Gateway address from the multi-purpose field
*/
public Gateway getGateway()
public Tier3Gateway getGateway()
{
return Gateway.fromValue(getMultiPurposeFieldValue());
return Tier3Gateway.fromValue(getMultiPurposeFieldValue());
}
/**

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -26,8 +26,7 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.Gateway;
import io.github.dsheirer.module.decode.dmr.message.type.Tier3Gateway;
import java.util.ArrayList;
import java.util.List;
@@ -81,9 +80,9 @@ public class UnknownAhoy extends Ahoy
/**
* Gateway address from the multi-purpose field
*/
public Gateway getGateway()
public Tier3Gateway getGateway()
{
return Gateway.fromValue(getMultiPurposeFieldValue());
return Tier3Gateway.fromValue(getMultiPurposeFieldValue());
}
/**

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,10 +23,9 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import java.util.ArrayList;
import java.util.List;
@@ -102,7 +101,7 @@ public class MassRegistration extends Announcement
{
if(mDestinationRadio == null)
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION_RADIO));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_RADIO));
}
return mDestinationRadio;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,11 +23,10 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -121,11 +120,11 @@ public class DuplexPrivateDataChannelGrant extends ChannelGrant
{
if(getMessage().get(TARGET_IS_SOURCE_ADDRESS_FLAG))
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(DESTINATION));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(DESTINATION));
}
else
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE));
}
}
@@ -141,11 +140,11 @@ public class DuplexPrivateDataChannelGrant extends ChannelGrant
{
if(getMessage().get(TARGET_IS_SOURCE_ADDRESS_FLAG))
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(SOURCE));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(SOURCE));
}
else
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION));
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,11 +23,10 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -121,11 +120,11 @@ public class DuplexPrivateVoiceChannelGrant extends ChannelGrant
{
if(getMessage().get(TARGET_IS_SOURCE_ADDRESS_FLAG))
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(DESTINATION));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(DESTINATION));
}
else
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE));
}
}
@@ -141,11 +140,11 @@ public class DuplexPrivateVoiceChannelGrant extends ChannelGrant
{
if(getMessage().get(TARGET_IS_SOURCE_ADDRESS_FLAG))
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(SOURCE));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(SOURCE));
}
else
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION));
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,11 +23,10 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -132,7 +131,7 @@ public class PrivateDataChannelGrant extends ChannelGrant
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE));
}
return mSourceRadio;
@@ -145,7 +144,7 @@ public class PrivateDataChannelGrant extends ChannelGrant
{
if(mDestinationRadio == null)
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION));
}
return mDestinationRadio;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -23,11 +23,10 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -118,7 +117,7 @@ public class PrivateVoiceChannelGrant extends ChannelGrant
{
if(mSourceRadio == null)
{
mSourceRadio = DMRRadio.createFrom(getMessage().getInt(SOURCE));
mSourceRadio = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE));
}
return mSourceRadio;
@@ -131,7 +130,7 @@ public class PrivateVoiceChannelGrant extends ChannelGrant
{
if(mDestinationRadio == null)
{
mDestinationRadio = DMRRadio.createTo(getMessage().getInt(DESTINATION));
mDestinationRadio = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION));
}
return mDestinationRadio;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -26,8 +26,8 @@ import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
import io.github.dsheirer.module.decode.dmr.message.type.AnnouncementType;
import io.github.dsheirer.module.decode.dmr.message.type.Vendor;
import java.util.Collections;
import java.util.List;
@@ -36,6 +36,8 @@ import java.util.List;
*/
public class MBCHeader extends CSBKMessage
{
private static final int[] ANNOUNCEMENT_TYPE = new int[]{16, 17, 18, 19, 20};
/**
* Constructs an instance
*
@@ -87,7 +89,7 @@ public class MBCHeader extends CSBKMessage
}
else
{
sb.append(" VENDOR:").append(vendor);
sb.append(" ").append(vendor);
}
}
@@ -97,6 +99,10 @@ public class MBCHeader extends CSBKMessage
{
sb.append(" UNKNOWN CSBKO:").append(getOpcodeValue());
}
else if(opcode == Opcode.HYTERA_08_ANNOUNCEMENT)
{
sb.append(" ANNOUNCEMENT:").append(AnnouncementType.fromValue(getMessage().getInt(ANNOUNCEMENT_TYPE)));
}
else
{
sb.append(" ").append(opcode);

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -23,8 +23,8 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.ServiceAccessPoint;
@@ -104,7 +104,7 @@ public abstract class PacketSequenceHeader extends DataHeader
}
else
{
mDestinationLLID = DMRRadio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
mDestinationLLID = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
}
}
@@ -119,7 +119,7 @@ public abstract class PacketSequenceHeader extends DataHeader
{
if(mSourceLLID == null)
{
mSourceLLID = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
mSourceLLID = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceLLID;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -24,12 +24,12 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.ResponseStatus;
import io.github.dsheirer.module.decode.dmr.message.type.ServiceAccessPoint;
import java.util.ArrayList;
import java.util.List;
@@ -47,6 +47,7 @@ public class ResponseDataHeader extends DataHeader
private static final int[] BLOCKS_TO_FOLLOW = new int[]{65, 66, 67, 68, 69, 70, 71};
private static final int[] CLASS_TYPE_STATUS = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
private ResponseStatus mResponseStatus;
private IntegerIdentifier mDestinationLLID;
private RadioIdentifier mSourceLLID;
private List<Identifier> mIdentifiers;
@@ -79,16 +80,29 @@ public class ResponseDataHeader extends DataHeader
sb.append(" FM:").append(getSourceLLID());
sb.append(" TO:").append(getDestinationLLID());
sb.append(" ").append(getServiceAccessPoint());
sb.append(" CLASS/TYPE/STATUS:").append(getClassTypeStatus());
sb.append(" BLOCKS TO FOLLOW:").append(getBlocksToFollow());
sb.append(" STATUS:").append(getResponseStatus());
sb.append(" - BLOCKS TO FOLLOW:").append(getBlocksToFollow());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Description of the response code.
* @return response status description.
*/
public ResponseStatus getResponseStatus()
{
if(mResponseStatus == null)
{
mResponseStatus = new ResponseStatus(getClassTypeStatus());
}
return mResponseStatus;
}
/**
* Class, Type and Status of response.
*
* TODO: create an enumeration for this value - see TS 102 361-3 for definitions
* @return
* @return integer value of the field.
*/
public int getClassTypeStatus()
{
@@ -132,7 +146,7 @@ public class ResponseDataHeader extends DataHeader
}
else
{
mDestinationLLID = DMRRadio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
mDestinationLLID = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
}
}
@@ -147,7 +161,7 @@ public class ResponseDataHeader extends DataHeader
{
if(mSourceLLID == null)
{
mSourceLLID = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
mSourceLLID = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceLLID;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -24,12 +24,11 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.ServiceAccessPoint;
import java.util.ArrayList;
import java.util.List;
@@ -134,7 +133,7 @@ public abstract class ShortDataHeader extends OctetDataHeader
}
else
{
mDestinationLLID = DMRRadio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
mDestinationLLID = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
}
}
@@ -149,7 +148,7 @@ public abstract class ShortDataHeader extends OctetDataHeader
{
if(mSourceLLID == null)
{
mSourceLLID = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
mSourceLLID = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceLLID;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -24,13 +24,12 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
import io.github.dsheirer.module.decode.dmr.identifier.DMRTalkgroup;
import io.github.dsheirer.module.decode.dmr.identifier.DMRUnitStatus;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import io.github.dsheirer.module.decode.dmr.message.type.ServiceAccessPoint;
import java.util.ArrayList;
import java.util.List;
@@ -158,7 +157,7 @@ public class StatusDataHeader extends DataHeader
}
else
{
mDestinationLLID = DMRRadio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
mDestinationLLID = DmrTier3Radio.createTo(getMessage().getInt(DESTINATION_IDENTIFIER));
}
}
@@ -173,7 +172,7 @@ public class StatusDataHeader extends DataHeader
{
if(mSourceLLID == null)
{
mSourceLLID = DMRRadio.createFrom(getMessage().getInt(SOURCE_RADIO));
mSourceLLID = DmrTier3Radio.createFrom(getMessage().getInt(SOURCE_RADIO));
}
return mSourceLLID;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -30,9 +30,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.header.ProprietaryDataH
*/
public class HyteraProprietaryDataHeader extends ProprietaryDataHeader
{
private static final int[] UNKNOWN_1 = new int[]{16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] UNKNOWN_2 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
/**
* Constructs an instance.
*
@@ -52,27 +49,14 @@ public class HyteraProprietaryDataHeader extends ProprietaryDataHeader
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("HYTERA PROPRIETARY DATA HEADER");
sb.append(" ").append(getServiceAccessPoint());
sb.append(" UNK1:").append(getUnknown1());
sb.append(" UNK2:").append(getUnknown2());
sb.append("CC:").append(getSlotType().getColorCode());
if(!isValid())
{
sb.append(" [CRC ERROR]");
}
sb.append(" HYTERA PROPRIETARY DATA HEADER");
sb.append(" SAP:").append(getServiceAccessPoint());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Unknown 8-bit field. This is likely the encryption type identifier.
*/
public int getUnknown1()
{
return getMessage().getInt(UNKNOWN_1);
}
/**
* Unknown 8-bit field. This is likely the encryption key identifier.
*/
public int getUnknown2()
{
return getMessage().getInt(UNKNOWN_2);
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -22,7 +22,6 @@ package io.github.dsheirer.module.decode.dmr.message.data.mbc;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessageFactory;
import io.github.dsheirer.module.decode.dmr.message.data.header.MBCHeader;
import java.util.ArrayList;
import java.util.List;
@@ -31,10 +30,10 @@ import java.util.List;
*/
public class MBCAssembler
{
private MBCHeader mTS0Header;
private MBCHeader mTS1Header;
private List<MBCContinuationBlock> mTS0ContinuationBlocks = new ArrayList<>();
private MBCHeader mTS2Header;
private List<MBCContinuationBlock> mTS1ContinuationBlocks = new ArrayList<>();
private List<MBCContinuationBlock> mTS2ContinuationBlocks = new ArrayList<>();
/**
* Constructs an instance
@@ -53,14 +52,14 @@ public class MBCAssembler
{
reset(header.getTimeslot());
if(header.getTimeslot() == 0)
{
mTS0Header = header;
}
else
if(header.getTimeslot() == 1)
{
mTS1Header = header;
}
else if(header.getTimeslot() == 2)
{
mTS2Header = header;
}
}
}
@@ -73,25 +72,7 @@ public class MBCAssembler
{
if(continuationBlock != null)
{
if(continuationBlock.getTimeslot() == 0)
{
mTS0ContinuationBlocks.add(continuationBlock);
if(continuationBlock.isLastBlock())
{
if(mTS0Header != null)
{
CSBKMessage csbk = CSBKMessageFactory.create(mTS0Header, new ArrayList<>(mTS0ContinuationBlocks));
reset(0);
return csbk;
}
else
{
reset(0);
}
}
}
else
if(continuationBlock.getTimeslot() == 1)
{
mTS1ContinuationBlocks.add(continuationBlock);
@@ -109,6 +90,24 @@ public class MBCAssembler
}
}
}
else if(continuationBlock.getTimeslot() == 2)
{
mTS2ContinuationBlocks.add(continuationBlock);
if(continuationBlock.isLastBlock())
{
if(mTS2Header != null)
{
CSBKMessage csbk = CSBKMessageFactory.create(mTS2Header, new ArrayList<>(mTS2ContinuationBlocks));
reset(2);
return csbk;
}
else
{
reset(2);
}
}
}
}
return null;
@@ -120,15 +119,15 @@ public class MBCAssembler
*/
public void reset(int timeslot)
{
if(timeslot == 0)
{
mTS0Header = null;
mTS0ContinuationBlocks.clear();
}
else
if(timeslot == 1)
{
mTS1Header = null;
mTS1ContinuationBlocks.clear();
}
else if(timeslot == 2)
{
mTS2Header = null;
mTS2ContinuationBlocks.clear();
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2020 Zhenyu Mao
* *****************************************************************************
* 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
@@ -15,7 +14,7 @@
*
* 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.dmr.message.data.mbc;
@@ -25,7 +24,6 @@ import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
import java.util.Collections;
import java.util.List;
@@ -52,7 +50,19 @@ public class MBCContinuationBlock extends DataMessage
{
StringBuilder sb = new StringBuilder();
sb.append("CC:").append(getSlotType().getColorCode());
sb.append(" MULTI-BLOCK CSBK CONTINUATION FINAL BLOCK:").append(isLastBlock() ? "YES" : "NO");
if(!isValid())
{
sb.append(" [CRC ERROR]");
}
sb.append(" MULTI-BLOCK CSBK CONTINUATION");
if(isLastBlock())
{
sb.append("-FINAL");
}
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}

View File

@@ -31,11 +31,13 @@ import io.github.dsheirer.module.decode.dmr.message.data.header.hytera.HyteraPro
import io.github.dsheirer.module.decode.dmr.message.data.header.motorola.MNISProprietaryDataHeader;
import io.github.dsheirer.module.decode.dmr.message.type.ApplicationType;
import io.github.dsheirer.module.decode.ip.UnknownPacket;
import io.github.dsheirer.module.decode.ip.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraTokenHeader;
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
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.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.xcmp.XCMPPacket;
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.xcmp.XCMPPacket;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
@@ -60,6 +62,7 @@ public class PacketSequenceMessageFactory
if(packetSequence.hasPacketSequenceHeader())
{
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
boolean confirmed = primaryHeader.isConfirmedData();
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
if(packet != null)
@@ -136,6 +139,7 @@ public class PacketSequenceMessageFactory
{
HeaderMessage secondaryHeader = packetSequence.getProprietaryDataHeader();
//MotoTRBO MNIS
if(secondaryHeader instanceof MNISProprietaryDataHeader)
{
ApplicationType applicationType = ((MNISProprietaryDataHeader)secondaryHeader).getApplicationType();
@@ -162,8 +166,18 @@ public class PacketSequenceMessageFactory
}
else if(secondaryHeader instanceof HyteraProprietaryDataHeader)
{
return new DMRPacketMessage(packetSequence, new UnknownPacket(packet, 0), packet,
packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp());
HyteraTokenHeader hyteraTokenHeader = new HyteraTokenHeader(packet);
if(hyteraTokenHeader.isSMSMessage())
{
return new DMRPacketMessage(packetSequence, new HyteraSmsPacket(hyteraTokenHeader), packet,
packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp());
}
else
{
return new DMRPacketMessage(packetSequence, new UnknownPacket(packet, 0), packet,
packetSequence.getTimeslot(), packetSequence.getPacketSequenceHeader().getTimestamp());
}
}
else
{
@@ -266,4 +280,31 @@ public class PacketSequenceMessageFactory
return null;
}
/**
* Inspects the first 7 bits of each data block to see if a contiguous data block sequence number pattern is
* present. This might indicate that the payload is a confirmed payload versus an unconfirmed payload when
* the packet sequence is not yet identified.
*
* @param dataBlocks to inspect.
* @return true if each of the data blocks have a sequential data block sequencing number.
*/
public static boolean isConfirmedDataBlocks(List<DataBlock> dataBlocks)
{
int sequenceNumber = 0;
for(DataBlock dataBlock: dataBlocks)
{
if(dataBlock.getDataBlockSerialNumber() == sequenceNumber)
{
sequenceNumber++;
}
else
{
return false;
}
}
return true;
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -25,7 +25,7 @@ package io.github.dsheirer.module.decode.dmr.message.type;
public enum AcknowledgeType
{
ACKNOWLEDGE(0, "ACKNOWLEDGED"),
NOT_ACKNOWLEDGE(1, "REJECTED/REFUSED"),
NOT_ACKNOWLEDGE(1, "REJECTED/REFUSED/NOT ACKNOWLEDGED"),
QUEUED(2, "QUEUED"),
WAIT(3, "WAIT"),
UNKNOWN(-1, "UNKNOWN");

View File

@@ -1,99 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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.dmr.message.type;
/**
* DMR Gateway Identifiers
*
* See: TS 102 361.4 paragraph A.4
*/
public enum Gateway
{
PSTNI(0xFFFEC0, "PSTN SERVICES - ALIGNED TIMING"),
PABXI(0xFFFEC1, "PABX SERVICES - ALIGNED TIMING"),
LINEI(0xFFFEC2, "LINE GATEWAY - ALIGNED TIMING"),
IPI(0xFFFEC3, "IP GATEWAY - ALIGNED TIMING"),
SUPLI(0xFFFEC4, "SUPPLEMENTARY DATA SERVICE"),
SDMI(0xFFFEC5, "UDT SHORT DATA SERVICE"),
REGI(0xFFFEC6, "REGISTRATION SERVICE"),
MSI(0xFFFEC7, "CALL DIVERSION TO MS"),
DIVERTI(0xFFFEC9, "CALL DIVERSION CANCEL"),
TSI(0xFFFECA, "TRUNK SYSTEM CONTROLLER"),
DISPATI(0xFFFECB, "SYSTEM DISPATCHER - ALIGNED TIMING"),
STUNI(0xFFFECC, "STUN/REVIVE MS ID"),
AUTHI(0xFFFECD, "AUTHENTICATION ID"),
GPI(0xFFFECE, "CALL DIVERSION TO TALKGROUP"),
KILLI(0xFFFECF, "KILL MS ID"),
PSTNDI(0xFFFED0, "PSTN SERVICES - OFFSET TIMING"),
PABXDI(0xFFFED1, "PABX SERVICES - OFFSET TIMING"),
LINEDI(0xFFFED2, "LINE GATEWAY - OFFSET TIMING"),
DISPATDI(0xFFFED3, "SYSTEM DISPATCHER - OFFSET TIMING"),
ALLMIS(0xFFFED4, "ALL MS AND TALKGROUPS"),
IPDI(0xFFFED5, "IP GATEWAY - OFFSET TIMING"),
DGNAI(0xFFFED6, "DYNAMIC GROUP NUMBER ASSIGNMENT"),
TATTSI(0xFFFED7, "TALKGROUP SUBSCRIBE/ATTACH SERVICE"),
ALLMSIDL(0xFFFFFD, "ALL SITE MS AS TALKGROUP"),
ALLMSIDZ(0xFFFFFE, "ALL SITE SUBSET MS AS TALKGROUP"),
ALLMSID(0xFFFFFF, "ALL MS AS TALKGROUP"),
UNKNOWN(-1, "UNKNOWN");
private int mAddress;
private String mLabel;
/**
* Constructs an instance
* @param address of the entry
* @param label for the entry
*/
Gateway(int address, String label)
{
mAddress = address;
mLabel = label;
}
@Override
public String toString()
{
return mLabel;
}
/**
* Address of the gateway identifier
*/
public int getAddress()
{
return mAddress;
}
/**
* Utility method to lookup the gateway from a value.
* @param value of the service type
* @return type or UNKNOWN
*/
public static Gateway fromValue(int value)
{
if(0 <= value && value <= 15)
{
return Gateway.values()[value];
}
return UNKNOWN;
}
}

View File

@@ -0,0 +1,76 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.type;
/**
* Hytera data encoding format values.
*/
public enum HyteraEncodeFormat
{
BINARY(0),
MOBILE_SUBSCRIBER_NUMBER(1),
BINARY_CODED_DECIMAL(2),
ISO_7(3),
ISO_8(4),
NMEA_GPS(5),
INTERNET_PROTOCOL(6),
UNICODE(7),
BYTES_8_BITS(8),
GBK(10), //GB2312 Simplified Chinese Characters
UNKNOWN(-1);
private int mValue;
/**
* Constructs an instance.
* @param value
*/
HyteraEncodeFormat(int value)
{
mValue = value;
}
/**
* Coded value for this format
*/
public int getValue()
{
return mValue;
}
/**
* Lookup the element from the value.
* @param value to lookup
* @return element or UNKNOWN
*/
public static HyteraEncodeFormat fromValue(int value)
{
if(0 <= value && value <= 8)
{
return HyteraEncodeFormat.values()[value];
}
else if(value == 10)
{
return GBK;
}
return UNKNOWN;
}
}

View File

@@ -0,0 +1,145 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.type;
/**
* Response header status parser class.
*
* ETSI TS 102 361-1 V2.5.1 Table 8.3: Response Packet Class, Type, and Status definitions
*/
public class ResponseStatus
{
private int mValue;
/**
* Constructs an instance
* @param value in the range of 0 - 255
*/
public ResponseStatus(int value)
{
mValue = value;
}
/**
* Status class indicating success, fail, or selective retry.
* @return status.
*/
public Status getStatus()
{
return Status.fromValue(mValue);
}
/**
* Type of status message, specific to each status class.
* @return
*/
public int getType()
{
return (mValue & 0x38) >> 3;
}
/**
* Value of the Status field.
* @return value
*/
public int getValue()
{
return (mValue & 0x07);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
switch(getStatus())
{
case ACK:
sb.append("SUCCESS - ALL BLOCKS THRU ").append(getValue()).append(" RECEIVED");
break;
case NACK:
switch(getType())
{
case 0:
sb.append("FAIL - ILLEGAL FORMAT - LAST VALID FSN:").append(getValue());
break;
case 1:
sb.append("FAIL - PACKET CRC FAIL - LAST VALID FSN:").append(getValue());
break;
case 2:
sb.append("FAIL - RECIPIENT MEMORY FULL - LAST VALID FSN:").append(getValue());
break;
case 3:
sb.append("FAIL - OUT OF SEQUENCE FSN:").append(getValue());
break;
case 4:
sb.append("FAIL - UNDELIVERABLE - LAST VALID FSN:").append(getValue());
break;
case 5:
sb.append("FAIL - PACKET OUT OF SEQUENCE - LAST VALID FSN:").append(getValue());
break;
case 6:
sb.append("FAIL - INVALID USER ON THIS SYSTEM");
break;
default:
sb.append("FAIL - UNKNOWN TYPE [").append(getType()).append("] VALUE [").append(getValue()).append("]");
break;
}
break;
case SACK:
sb.append("SELECTIVE RETRY - LAST VALID FSN:").append(getValue());
break;
case RESERVED:
sb.append("UNKNOWN/RESERVED WITH TYPE [").append(getType()).append("] VALUE [").append(getValue()).append("]");
break;
}
return sb.toString();
}
/**
* Status for the response, from the 2 MSB of the value.
*/
private enum Status
{
ACK, //Success
NACK, //Fail
SACK, //Selective Retry
RESERVED;
/**
* Lookup the status from the value.
* @param value of the status
* @return lookup or RESERVED
*/
public static Status fromValue(int value)
{
int masked = (value & 0xC0) >> 6;
if(0 <= masked && masked <= 2)
{
return Status.values()[masked];
}
return RESERVED;
}
}
}

View File

@@ -0,0 +1,135 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.type;
/**
* Known DMR gateways and system identities defined in ETSI TS 102 361-4 Table A.4 Tier III Gateways/Identifiers
*/
public enum Tier3Gateway
{
PSTNI(0xFFFEC0, "PSTN GATEWAY"),
PABXI(0xFFFEC1, "PABX GATEWAY"),
LINEI(0xFFFEC2, "LINE GATEWAY"),
IPI(0xFFFEC3, "IP GATEWAY"),
SUPLI(0xFFFEC4, "SUPPLEMENTARY DATA SERVICE"),
SDMI(0xFFFEC5, "UDT SHORT DATA SERVICE"),
REGI(0xFFFEC6, "REGISTRATION SERVICE"),
MSI(0xFFFEC7, "CALL DIVERSION TO MS GATEWAY"),
DIVERTI(0xFFFEC9, "CALL DIVERSION CANCELLATION"),
TSI(0xFFFECA, "TRUNKING SYSTEM CONTROLLER"),
DISPATI(0xFFFECB, "SYSTEM DISPATCHER"),
STUNI(0xFFFECC, "MS STUN/REVIVE"),
AUTHI(0xFFFECD, "AUTHENTICATION"),
GPI(0xFFFECE, "CALL DIVERSION TO TALKGROUP GATEWAY"),
KILLI(0xFFFECF, "MS KILL"),
PSTNDI(0xFFFED0, "PSTN-D GATEWAY"),
PABXDI(0xFFFED1, "PABX-D GATEWAY"),
LINEDI(0xFFFED2, "LINE-D GATEWAY"),
DISPATDI(0xFFFED3, "SYSTEM DISPATCHER-D"),
ALLMSI(0xFFFED4, "ALL RADIOS/TALKGROUPS"),
IPDI(0xFFFED5, "IP-D GATEWAY"),
DGNAI(0xFFFED6, "DYNAMIC GROUP NUMBER ASSIGNMENT"),
TATTSI(0xFFFED7, "TALKGROUP SUBSCRIBE/ATTACH SERVICE"),
ALLMSIDL(0xFFFFFD, "ALL RADIOS AT SITE"),
ALLMSIDZ(0xFFFFFE, "ALL RADIOS IN ZONE"),
ALLMSID(0xFFFFFF, "ALL RADIOS IN SYSTEM"),
RESERVED(0x0, "RESERVED");
private int mValue;
private String mLabel;
/**
* Constructs an instance
* @param value of the identifier
* @param label to describe the identifier
*/
Tier3Gateway(int value, String label)
{
mValue = value;
mLabel = label;
}
/**
* Indicates if the radio is a known gateway identifier
* @param value to test
* @return true if the value is a known gateway identifier
*/
public static boolean isGateway(int value)
{
if(value >= PSTNI.getValue())
{
for(Tier3Gateway gateway: Tier3Gateway.values())
{
if(gateway.matches(value))
{
return true;
}
}
}
return false;
}
/**
* Lookup the DMR Tier-III gateway from a value.
* @param value of the radio identifier
* @return the known gateway or RESERVED if the value doesn't match a known gateway.
*/
public static Tier3Gateway fromValue(int value)
{
for(Tier3Gateway gateway: Tier3Gateway.values())
{
if(gateway.matches(value))
{
return gateway;
}
}
return RESERVED;
}
/**
* Indicates if this entry matches the specified value.
* @param value to test for match
* @return true if the value matches.
*/
public boolean matches(int value)
{
return mValue == value;
}
/**
* Numeric value for the entry.
* @return value.
*/
public int getValue()
{
return mValue;
}
/**
* Print friendly label for the value.
* @return label
*/
public String getLabel()
{
return mLabel;
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2020 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,22 +14,22 @@
*
* 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;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.module.decode.ip.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.cellocator.MCGPHeader;
import io.github.dsheirer.module.decode.ip.cellocator.MCGPMessageFactory;
import io.github.dsheirer.module.decode.ip.icmp.ICMPPacket;
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.lrrp.LRRPPacket;
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.xcmp.XCMPPacket;
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
import io.github.dsheirer.module.decode.ip.udp.UDPPort;
import io.github.dsheirer.module.decode.ip.xcmp.XCMPPacket;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketHeader;
import io.github.dsheirer.module.decode.p25.reference.IPHeaderCompression;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;
@@ -41,7 +39,7 @@ public class UnknownPacket implements IPacket
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("PACKET:");
sb.append("UNKNOWN PACKET:");
if(getMessage().size() > getOffset())
{

View File

@@ -0,0 +1,77 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Destination Identifier
*/
public class DestinationId extends HyteraToken
{
private static final int[] CONTENT_LENGTH = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private static final int CONTENT_START = 32;
private static final int[] ID = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public DestinationId(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("DESTINATION:").append(getId());
return sb.toString();
}
/**
* Length of the content field in bytes.
* @return content field length.
*/
public int getContentLength()
{
return mMessage.getInt(CONTENT_LENGTH);
}
/**
* Identify value parsed from the field content.
*
* Note: although this field has a content length specifier, I've hard-coded it to parse a 32-bit value for now
* until we find need to make it more fancy.
*/
public int getId()
{
return mMessage.getInt(ID);
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.ID_DESTINATION;
}
}

View File

@@ -0,0 +1,82 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.module.decode.dmr.message.type.HyteraEncodeFormat;
/**
* Encoding
*/
public class Encoding extends HyteraToken
{
private static final int[] CONTENT_LENGTH = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] ENCODING = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public Encoding(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("ENCODING FORMAT:").append(getEncoding());
return sb.toString();
}
/**
* Length of the content field in bytes.
* @return content field length.
*/
public int getContentLength()
{
return mMessage.getInt(CONTENT_LENGTH);
}
/**
* Hytera encoding format
*/
public HyteraEncodeFormat getEncoding()
{
return HyteraEncodeFormat.fromValue(getEncodingValue());
}
/**
* Encoding lookup value.
* @return value
*/
private int getEncodingValue()
{
return mMessage.getInt(ENCODING);
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.ENCODING;
}
}

View File

@@ -0,0 +1,44 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Base abstract class for a field parsed from a Hytera Data Service message.
*/
public abstract class HyteraToken
{
protected CorrectedBinaryMessage mMessage;
/**
* Constructs an instance
* @param message containing both the token, run-length, and content
*/
public HyteraToken(CorrectedBinaryMessage message)
{
mMessage = message;
}
/**
* Identifies the token or field type for this field.
*/
abstract HyteraTokenType getTokenType();
}

View File

@@ -0,0 +1,48 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Constructs Hytera token field instances.
*/
public class HyteraTokenFactory
{
/**
* Creates a new parsed field instance
* @param token that identifies the field type
* @param message fragment that includes the token, run-length, and field content.
* @return parsed field instance
*/
public static HyteraToken getField(HyteraTokenType token, CorrectedBinaryMessage message)
{
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);
default -> new Unknown(message);
};
}
}

View File

@@ -0,0 +1,169 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import com.google.common.base.Joiner;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.module.decode.ip.IHeader;
import java.util.ArrayList;
import java.util.List;
/**
* Message parser/decoder for Hytera run-length encoded payload message carried in Proprietary Data packet sequences.
*/
public class HyteraTokenHeader implements IHeader
{
private static int[] TWO_BYTES = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
private static int[] FOUR_BYTES = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private CorrectedBinaryMessage mMessage;
private List<HyteraToken> mParsedTokens = new ArrayList<>();
/**
* Constructs an instance.
* @param message assembled from data blocks.
*/
public HyteraTokenHeader(CorrectedBinaryMessage message)
{
mMessage = message;
parse();
}
@Override
public boolean isValid()
{
return false;
}
@Override
public int getLength()
{
return 0;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("HYTERA TOKEN HEADER: ").append(Joiner.on(",").join(getParsedTokens()));
return sb.toString();
}
/**
* Indicates if this header contains enough tokens to create an SMS message
*/
public boolean isSMSMessage()
{
return hasTokens(HyteraTokenType.MESSAGE_HEADER, HyteraTokenType.ENCODING, HyteraTokenType.PAYLOAD);
}
/**
* Indicates if this message has all of the specified tokens.
* @param types one or more tokens.
* @return true if all tokens are present in this payload.
*/
public boolean hasTokens(HyteraTokenType ... types)
{
boolean hasToken = true;
for(HyteraTokenType type: types)
{
hasToken &= hasToken(type);
}
return hasToken;
}
/**
* Indicates if this payload has the specified token type.
* @param type of token to check
* @return true if this payload has a non-null instance of the token type
*/
public boolean hasToken(HyteraTokenType type)
{
return getTokenByType(type) != null;
}
/**
* Retrieves tokens by type.
* @param type of token
* @return token or null
*/
public HyteraToken getTokenByType(HyteraTokenType type)
{
for(HyteraToken token: mParsedTokens)
{
if(token.getTokenType() == type)
{
return token;
}
}
return null;
}
/**
* Fields parsed from this SDS message
* @return fields
*/
public List<HyteraToken> getParsedTokens()
{
return mParsedTokens;
}
private void parse()
{
int offset = 0;
while(offset < mMessage.length())
{
int opcode = mMessage.getInt(TWO_BYTES, offset);
int length = 2 * 8; //Opcode length in bits
HyteraTokenType token = HyteraTokenType.fromOpcode(opcode);
if(token != HyteraTokenType.UNKNOWN)
{
if(token.isFixedLength())
{
length += token.getFieldLength() * 8; //Content length
}
else
{
int contentLength = mMessage.getInt(TWO_BYTES, offset + length);
length += 2 * 8; //Content length 4 bytes
length += contentLength * 8; //Actual content
}
HyteraToken field = HyteraTokenFactory.getField(token, mMessage.getSubMessage(offset, offset + length + 1));
offset += length;
mParsedTokens.add(field);
}
else
{
CorrectedBinaryMessage remainder = mMessage.getSubMessage(offset, mMessage.length() + 1);
HyteraToken unknown = HyteraTokenFactory.getField(HyteraTokenType.UNKNOWN, remainder);
mParsedTokens.add(unknown);
offset = mMessage.length();
}
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
/**
* Hytera run-length encoding tokens.
*
* Each token implies a parseable field that is optionally followed by a 4-byte field content length and then followed
* by the contents of the field.
*
* In the enum entries below, a field length of -1 indicates the opcode is followed by a 4-byte content length value.
*/
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
ID_MESSAGE(0x0001, -1),
ID_SOURCE(0x0002, -1),
ID_DESTINATION(0x0003, -1),
ENCODING(0x0101, -1),
PAYLOAD(0x0102, -1),
UNKNOWN(-1, -1);
private int mOpcode;
private int mFieldLength;
/**
* Constructs an instance
* @param opcode value
* @param fieldLength in bytes, or -1 to indicate the opcode is followed by a 4-byte run length value.
*/
HyteraTokenType(int opcode, int fieldLength)
{
mOpcode = opcode;
mFieldLength = fieldLength;
}
/**
* Opcode value
* @return integer value of the opcode
*/
public int getOpcode()
{
return mOpcode;
}
/**
* Length of the field content in bytes that follow the opcode. When this value is a -1, then the opcode is
* followed by a 4-byte value that indicates the length of the content field that follows immediately after.
* @return field length
*/
public int getFieldLength()
{
return mFieldLength;
}
/**
* Indicates if this is a fixed length field.
*/
public boolean isFixedLength()
{
return mFieldLength >= 0;
}
/**
* Lookup a token from the opcode value.
* @param opcode value to lookup
* @return token or UNKNOWN
*/
public static HyteraTokenType fromOpcode(int opcode)
{
for(HyteraTokenType token: HyteraTokenType.values())
{
if(token.getOpcode() == opcode)
{
return token;
}
}
return UNKNOWN;
}
}

View File

@@ -0,0 +1,83 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* 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
{
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_FLAGS = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public MessageHeader(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("MESSAGE HEADER LENGTH:").append(getMessageLength());
sb.append(" FLAGS:").append(getFlags());
return sb.toString();
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.MESSAGE_HEADER;
}
/**
* Likely a set of bit fields that indicate optional flags for the message like:
* - Acknowledge Receipt (boolean)
* - Priority/Flash (boolean)
* - Encryption Type (enum of at least 4 values, ie 2+ bits)
* - Emergency (boolean)
* @return
*/
public String getFlags()
{
return mMessage.getHex(MESSAGE_FLAGS, 4);
}
/**
* Overall message length in bytes
* @return message length.
*/
public int getMessageLength()
{
return mMessage.getInt(MESSAGE_LENGTH);
}
}

View File

@@ -0,0 +1,78 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Message Identifier
*/
public class MessageId extends HyteraToken
{
private static final int[] CONTENT_LENGTH = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private static final int CONTENT_START = 32;
private static final int[] ID = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public MessageId(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("MESSAGE ID:").append(getId());
return sb.toString();
}
/**
* Length of the content field in bytes.
*
* @return content field length.
*/
public int getContentLength()
{
return mMessage.getInt(CONTENT_LENGTH);
}
/**
* Identify value parsed from the field content.
* <p>
* Note: although this field has a content length specifier, I've hard-coded it to parse a 32-bit value for now
* until we find need to make it more fancy.
*/
public int getId()
{
return mMessage.getInt(ID);
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.ID_MESSAGE;
}
}

View File

@@ -0,0 +1,60 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Payload bytes
*/
public class Payload extends HyteraToken
{
private static final int[] CONTENT_LENGTH = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private static final int PAYLOAD_START = 32;
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public Payload(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("PAYLOAD:").append(getPayload().toHexString());
return sb.toString();
}
public CorrectedBinaryMessage getPayload()
{
return mMessage.getSubMessage(PAYLOAD_START, mMessage.length() + 1);
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.PAYLOAD;
}
}

View File

@@ -0,0 +1,77 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Source Identifier
*/
public class SourceId extends HyteraToken
{
private static final int[] CONTENT_LENGTH = new int[]{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
private static final int CONTENT_START = 32;
private static final int[] ID = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public SourceId(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("SOURCE:").append(getId());
return sb.toString();
}
/**
* Length of the content field in bytes.
* @return content field length.
*/
public int getContentLength()
{
return mMessage.getInt(CONTENT_LENGTH);
}
/**
* Identify value parsed from the field content.
*
* Note: although this field has a content length specifier, I've hard-coded it to parse a 32-bit value for now
* until we find need to make it more fancy.
*/
public int getId()
{
return mMessage.getInt(ID);
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.ID_SOURCE;
}
}

View File

@@ -0,0 +1,52 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sds;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/**
* Unknown field parsed from a Hytera Data Service message
*/
public class Unknown extends HyteraToken
{
/**
* Constructs an instance
*
* @param message containing both the token, run-length, and content
*/
public Unknown(CorrectedBinaryMessage message)
{
super(message);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("UNKNOWN:").append(mMessage.toHexString());
return sb.toString();
}
@Override
HyteraTokenType getTokenType()
{
return HyteraTokenType.UNKNOWN;
}
}

View File

@@ -0,0 +1,321 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.hytera.sms;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.module.decode.dmr.identifier.DmrTier3Radio;
import io.github.dsheirer.module.decode.ip.IPacket;
import io.github.dsheirer.module.decode.ip.hytera.sds.DestinationId;
import io.github.dsheirer.module.decode.ip.hytera.sds.Encoding;
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraToken;
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraTokenHeader;
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraTokenType;
import io.github.dsheirer.module.decode.ip.hytera.sds.MessageId;
import io.github.dsheirer.module.decode.ip.hytera.sds.Payload;
import io.github.dsheirer.module.decode.ip.hytera.sds.SourceId;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* Hytera SDS Long Data Message with SMS encoded payload
*/
public class HyteraSmsPacket implements IPacket
{
private HyteraTokenHeader mHeader;
private String mSMS;
private RadioIdentifier mSourceRadio;
private RadioIdentifier mDestinationRadio;
private List<Identifier> mIdentifiers;
private MessageId mMessageId;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header to the packet within the message
*/
public HyteraSmsPacket(HyteraTokenHeader header)
{
mHeader = header;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("HYTERA LONG SMS");
if(hasSMS())
{
sb.append(":").append(getSMS());
}
if(hasMessageId())
{
sb.append(" ID:").append(getMessageId().getId());
}
if(hasSource())
{
sb.append(" SOURCE:").append(getSource());
}
if(hasDestination())
{
sb.append("DESTINATION:").append(getDestination());
}
return sb.toString();
}
@Override
public HyteraTokenHeader getHeader()
{
return mHeader;
}
/**
* Message ID for the SMS message
*/
public MessageId getMessageId()
{
if(mMessageId == null)
{
HyteraToken idToken = getHeader().getTokenByType(HyteraTokenType.ID_MESSAGE);
if(idToken instanceof MessageId messageId)
{
mMessageId = messageId;
}
}
return mMessageId;
}
/**
* Indicates if the SMS has a message ID
* @return message ID
*/
public boolean hasMessageId()
{
return getMessageId() != null;
}
/**
* Source radio identifier for the SMS message
*/
public RadioIdentifier getSource()
{
if(mSourceRadio == null)
{
HyteraToken sourceToken = getHeader().getTokenByType(HyteraTokenType.ID_SOURCE);
if(sourceToken instanceof SourceId sourceId)
{
mSourceRadio = DmrTier3Radio.createFrom(sourceId.getId());
}
}
return mSourceRadio;
}
/**
* Indicates if the SMS message has a source identifier
*/
public boolean hasSource()
{
return getSource() != null;
}
/**
* Destination radio identifier for the SMS message
*/
public RadioIdentifier getDestination()
{
if(mDestinationRadio == null)
{
HyteraToken destinationToken = getHeader().getTokenByType(HyteraTokenType.ID_DESTINATION);
if(destinationToken instanceof DestinationId destinationId)
{
mDestinationRadio = DmrTier3Radio.createTo(destinationId.getId());
}
}
return mDestinationRadio;
}
/**
* Indicates if the SMS message has a destination identifier
*/
public boolean hasDestination()
{
return getDestination() != null;
}
/**
* Indicates if this payload has an SMS message
* @return true if SMS
*/
public boolean hasSMS()
{
return getSMS() != null;
}
/**
* Parsed SMS message
* @return
*/
public String getSMS()
{
if(mSMS == null)
{
HyteraToken encodingToken = getHeader().getTokenByType(HyteraTokenType.ENCODING);
if(encodingToken instanceof Encoding encoding)
{
HyteraToken payloadToken = getHeader().getTokenByType(HyteraTokenType.PAYLOAD);
if(payloadToken instanceof Payload payload)
{
switch(encoding.getEncoding())
{
case ISO_7:
mSMS = parseISO7Payload(payload.getPayload());
break;
case ISO_8:
mSMS = parseISO8Payload(payload.getPayload());
break;
case UNICODE:
mSMS = parseUnicodePayload(payload.getPayload());
break;
case GBK:
mSMS = parseGB2312Payload(payload.getPayload());
break;
}
}
}
}
return mSMS;
}
/**
* Parses a GB2312 Simplified Chinese Characters 16-bit payload from the message.
* @return parsed message
*/
private String parseGB2312Payload(BinaryMessage message)
{
int length = message.size();
if(length > 16)
{
try
{
return message.parseGB2312(0, (length / 16));
}
catch(UnsupportedEncodingException uee)
{
return "(GB2312 unsupported decoding)";
}
}
else
{
return "(insufficient data)";
}
}
/**
* Parses a unicode 16-bit payload from the message.
* @return parsed message
*/
private String parseUnicodePayload(BinaryMessage message)
{
int length = message.size();
if(length > 16)
{
return message.parseUnicode(0, (length / 16));
}
else
{
return "(insufficient data)";
}
}
/**
* Parses an ISO-7 payload from the message.
* @return parsed message
*/
private String parseISO7Payload(BinaryMessage message)
{
int length = message.size();
if(length > 7)
{
return message.parseISO7(0, (length / 7));
}
else
{
return "(insufficient data)";
}
}
/**
* Parses an ISO-8 payload from the message.
* @return parsed message
*/
private String parseISO8Payload(BinaryMessage message)
{
int length = message.size();
if(length > 8)
{
return message.parseISO8(0, (length / 8));
}
else
{
return "(insufficient data)";
}
}
@Override
public IPacket getPayload() {return null;}
@Override
public boolean hasPayload() {return false;}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
if(hasSource())
{
mIdentifiers.add(getSource());
}
if(hasDestination())
{
mIdentifiers.add(getDestination());
}
}
return mIdentifiers;
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -17,12 +17,11 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.ip.Header;
import java.util.List;
public abstract class ARSHeader extends Header

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,10 +14,10 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,10 +14,10 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
public enum ARSPDUType
{

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,16 +14,15 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
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.util.List;
public class ARSPacket extends Packet

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -17,13 +17,13 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.ip.ars.identifier.ARSDevice;
import io.github.dsheirer.module.decode.ip.ars.identifier.ARSPassword;
import io.github.dsheirer.module.decode.ip.ars.identifier.ARSUser;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier.ARSDevice;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier.ARSPassword;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier.ARSUser;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -17,11 +17,10 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -17,13 +17,13 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.ip.ars.identifier.ARSDevice;
import io.github.dsheirer.module.decode.ip.ars.identifier.ARSPassword;
import io.github.dsheirer.module.decode.ip.ars.identifier.ARSUser;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier.ARSDevice;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier.ARSPassword;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier.ARSUser;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* 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
@@ -15,14 +14,13 @@
*
* 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.ars;
package io.github.dsheirer.module.decode.ip.mototrbo.ars;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import java.util.Collections;
import java.util.List;

View File

@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars.identifier;
package io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.IdentifierClass;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars.identifier;
package io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.IdentifierClass;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.ars.identifier;
package io.github.dsheirer.module.decode.ip.mototrbo.ars.identifier;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.IdentifierClass;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -17,12 +17,11 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.lrrp;
package io.github.dsheirer.module.decode.ip.mototrbo.lrrp;
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;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2021 Dennis Sheirer
* 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
@@ -17,17 +17,16 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.lrrp;
package io.github.dsheirer.module.decode.ip.mototrbo.lrrp;
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 io.github.dsheirer.module.decode.ip.lrrp.token.RequestedTokens;
import io.github.dsheirer.module.decode.ip.lrrp.token.Token;
import io.github.dsheirer.module.decode.ip.lrrp.token.TokenFactory;
import io.github.dsheirer.module.decode.ip.lrrp.token.TokenType;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token.RequestedTokens;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token.Token;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token.TokenFactory;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token.TokenType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.lrrp;
package io.github.dsheirer.module.decode.ip.mototrbo.lrrp;
/**
* LRRP Packet Type enumeration

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2021 Dennis Sheirer
* 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
@@ -17,10 +17,9 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.lrrp.token;
package io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import java.text.DecimalFormat;
/**

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2021 Dennis Sheirer
* 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
@@ -17,10 +17,9 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ip.lrrp.token;
package io.github.dsheirer.module.decode.ip.mototrbo.lrrp.token;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import java.text.DecimalFormat;
/**

Some files were not shown because too many files have changed in this diff Show More