#1507 Resolves issue with Cap+ LSN parsing. Updates Cap+ Site Status and adds RAS bit handling. (#1536)

Co-authored-by: Matthew Foster <matthew.foster@noaa.gov>
This commit is contained in:
wedgef5
2023-05-14 08:03:49 -04:00
committed by GitHub
parent b39dd1a19f
commit 3df8d60eed
66 changed files with 796 additions and 252 deletions

View File

@@ -44,7 +44,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAloha;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusNeighbors;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusSystemStatus;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusSiteStatus;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.ConnectPlusDataChannelGrant;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.ConnectPlusVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Aloha;
@@ -673,10 +673,10 @@ public class DMRDecoderState extends TimeslotDecoderState
updateRestChannel(((CapacityPlusNeighbors)csbk).getRestChannel());
}
break;
case MOTOROLA_CAPPLUS_SYSTEM_STATUS:
if(csbk instanceof CapacityPlusSystemStatus)
case MOTOROLA_CAPPLUS_SITE_STATUS:
if(csbk instanceof CapacityPlusSiteStatus)
{
CapacityPlusSystemStatus cpss = (CapacityPlusSystemStatus)csbk;
CapacityPlusSiteStatus cpss = (CapacityPlusSiteStatus)csbk;
//Channel rotation monitor normally uses only CONTROL state, so when we detect that we're a
//Capacity plus system, add ACTIVE as an active state to the monitor. This can be requested repeatedly.

View File

@@ -377,7 +377,7 @@ public class DMRMessageFramer implements Listener<Dibit>, IDMRBurstDetectListene
public static void main(String[] args)
{
String path = "/media/denny/Lexar/Recordings/DMR/";
String path = "/media/denny/T7 Shield/Recordings/DMR/";
//Con+ Traffic + Voice
// String file = path + "20200513_143340_9600BPS_DMR_SaiaNet_Onondaga_Control.bits"; //Enh GPS Revert Window Annce
@@ -385,7 +385,7 @@ public class DMRMessageFramer implements Listener<Dibit>, IDMRBurstDetectListene
// String file = path + "20200514_063507_9600BPS_DMR_SaiaNet_Onondaga_LCN_3_Control.bits"; //GPS Window Grant 2579
// String file = path + "20200514_064224_9600BPS_DMR_SaiaNet_Onondaga_LCN_3_Control.bits"; //GPS Window Grant 5056035
// String file = path + "20200514_131623_9600BPS_DMR_SaiaNet_Onondaga_LCN_3_Control.bits"; //GPS Grant: 5074193
String file = path + "20200514_133947_9600BPS_DMR_SaiaNet_Onondaga_LCN_4.bits"; //<<<<<<<------ Basic Encryption
// String file = path + "SaiaNet/20200514_133947_9600BPS_DMR_SaiaNet_Onondaga_LCN_4.bits"; //<<<<<<<------ Basic Encryption
// String file = path + "20200514_142249_9600BPS_DMR_SaiaNet_Onondaga_LCN_4.bits";
// String file = path + "20200514_144534_9600BPS_DMR_SaiaNet_Onondaga_LCN_3_Control.bits"; //Con+ Control w/GPS Window Announce
@@ -395,12 +395,16 @@ public class DMRMessageFramer implements Listener<Dibit>, IDMRBurstDetectListene
//Cap-Max Tier III CC
// String file = path + "20200710_053632_9600BPS_DMR_Niles_Radio_Coconino_Control.bits";
//Cap+ Multi-Site 1 - Traffic LCN 2
// String file = path + "20200716_210133_9600BPS_DMR_Aerowave_Technologies_Dallas_LCN_2.bits";
// String file = path + "20200716_212309_9600BPS_DMR_Aerowave_Technologies_Dallas_LCN_2.bits";
//Cap+ Multi-Site 1 - Data Revert LSN1/2 with CSBKO 62 Site status
// String file = path + "Texas_Dallas_Aerowave_Capacity_Plus_Multi_Site/20200716_210133_9600BPS_DMR_Aerowave_Technologies_Dallas_LCN_2.bits";
// String file = path + "Texas_Dallas_Aerowave_Capacity_Plus_Multi_Site/20200716_210845_9600BPS_DMR_Aerowave_Technologies_Dallas_LCN_2.bits";
// String file = path + "Texas_Dallas_Aerowave_Capacity_Plus_Multi_Site/20200716_212309_9600BPS_DMR_Aerowave_Technologies_Dallas_LCN_2.bits";
//Cap+ Multi-Site Enhanced GPS Channel
// String file = path + "20200714_224018_9600BPS_DMR_Farmers_Electric_Cooperative_Hunt_LCN_3.bits"; //This may have PLL mis-align issues
//Cap+ Multi-Site Enhanced Data Revert Channel (no CSBKO 62)
// String file = path + "Texas_Hunt_Farmers_Electric_Capacity_Plus_Multi_Site/20200714_224018_9600BPS_DMR_Farmers_Electric_Cooperative_Hunt_LCN_3.bits"; //This may have PLL mis-align issues
//Cap+ Multi-Site - Dallas Unknown
String file = path + "Texas_Dallas_Unknown_1_Capacity_Plus_Multi_Site/20200716_211233_9600BPS_DMR_Dallas_Unk_Dallas_Unk.bits";
//Hytera Short Data = Proprietary (encrypted)
// String file = path + "20200716_222839_9600BPS_DMR_SystemUnk_SiteUnk_Unk.bits"; //Hytera short data packets

View File

@@ -111,6 +111,9 @@ public class DMRMessageProcessor implements Listener<IMessage>
}
}
//Now that the message has been (potentially) enriched, dispatch it to the modules
dispatch(message);
//Extract the Full Link Control message fragment from the Voice with embedded signalling message
if(message instanceof VoiceEMBMessage)
{
@@ -202,9 +205,6 @@ public class DMRMessageProcessor implements Listener<IMessage>
{
dispatch(mTalkerAliasAssembler.process(flc));
}
//Now that the message has been (potentially) enriched, dispatch it to the modules
dispatch(message);
}
/**

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * 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/>
* * *****************************************************************************
* 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.channel;
@@ -87,7 +84,7 @@ public class DMRLogicalChannel extends DMRChannel
{
StringBuilder sb = new StringBuilder();
sb.append("LSN:").append(getLogicalSlotNumber());
sb.append(" LCN:").append(getValue());
// sb.append(" LCN:").append(getValue());
return sb.toString();
}
}

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
@@ -28,6 +28,9 @@ import io.github.dsheirer.module.decode.dmr.message.DMRBurst;
*/
public abstract class DataMessage extends DMRBurst
{
//R2, R1, R0 extracted from the BPTC extraction process. Note: message length remains at 96, even though these 3x bits
//are set at the end of the message.
private static final int[] BPTC_RESERVED_BITS = new int[]{96, 97, 98};
private SlotType mSlotType;
/**
@@ -54,4 +57,24 @@ public abstract class DataMessage extends DMRBurst
{
return mSlotType;
}
/**
* 3x reserved bits that are left-over from the BPTC encode/decode process that can be used to hold values like
* Moto RAS indicator. The message length from the BPTC decoder is set to 96 and these 3x bits are appended to
* the end as overage. However, to keep the message.toString() correct, we specify the length as 96.
* @return reserved value.
*/
public int getBPTCReservedBits()
{
return getMessage().getInt(BPTC_RESERVED_BITS);
}
/**
* Indicates if the BPTC reserved bits value is anything other than 0.
* @return true if non-zero.
*/
public boolean hasRAS()
{
return getBPTCReservedBits() != 0;
}
}

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,7 +26,6 @@ import io.github.dsheirer.module.decode.dmr.message.CACH;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessageFactory;
import io.github.dsheirer.module.decode.dmr.message.type.DataType;
import java.util.Collections;
import java.util.List;
@@ -71,6 +70,10 @@ public abstract class DataMessageWithLinkControl extends DataMessage
public String toString()
{
StringBuilder sb = new StringBuilder();
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(getSlotType());
sb.append(" ").append(getLCMessage());
return sb.toString();

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;
@@ -23,7 +22,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import java.util.Collections;
import java.util.List;
@@ -50,6 +48,12 @@ public class IDLEMessage extends DataMessage
{
StringBuilder sb = new StringBuilder();
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" IDLE");
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
@@ -23,7 +23,6 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.CACH;
import java.util.Collections;
import java.util.List;
@@ -61,6 +60,11 @@ public class UnknownDataMessage extends DataMessage
sb.append("TS").append(getTimeslot());
}
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" ").append(getSlotType());
sb.append(" ").append(getMessage().toHexString());

View File

@@ -75,6 +75,11 @@ public abstract class DataBlock extends DataMessage
{
StringBuilder sb = new StringBuilder();
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" ").append(getSlotType());
sb.append(" ").append(getMessage().toHexString());

View File

@@ -39,7 +39,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityP
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusDataRevertWindowGrant;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusNeighbors;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusPreamble;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusSystemStatus;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusSiteStatus;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.ConnectPlusCSBKO_16;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.ConnectPlusDataChannelGrant;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.ConnectPlusDataRevertWindowAnnouncement;
@@ -268,8 +268,8 @@ public class CSBKMessageFactory
case MOTOROLA_CAPPLUS_PREAMBLE:
csbk = new CapacityPlusPreamble(pattern, message, cach, slotType, timestamp, timeslot);
break;
case MOTOROLA_CAPPLUS_SYSTEM_STATUS:
csbk = new CapacityPlusSystemStatus(pattern, message, cach, slotType, timestamp, timeslot);
case MOTOROLA_CAPPLUS_SITE_STATUS:
csbk = new CapacityPlusSiteStatus(pattern, message, cach, slotType, timestamp, timeslot);
break;
case MOTOROLA_CAPPLUS_DATA_WINDOW_ANNOUNCEMENT:
csbk = new CapacityPlusDataRevertWindowAnnouncement(pattern, message, cach, slotType, timestamp, timeslot);

View File

@@ -87,7 +87,7 @@ public enum Opcode
MOTOROLA_CAPPLUS_NEIGHBOR_REPORT(Vendor.MOTOROLA_CAPACITY_PLUS, 59, "NEIGHBOR REPORT"),
MOTOROLA_CAPPLUS_CSBKO_60(Vendor.MOTOROLA_CAPACITY_PLUS, 60, "CSBKO 60"),
MOTOROLA_CAPPLUS_PREAMBLE(Vendor.MOTOROLA_CAPACITY_PLUS, 61, "PREAMBLE"),
MOTOROLA_CAPPLUS_SYSTEM_STATUS(Vendor.MOTOROLA_CAPACITY_PLUS, 62, "SYSTEM STATUS"),
MOTOROLA_CAPPLUS_SITE_STATUS(Vendor.MOTOROLA_CAPACITY_PLUS, 62, "SITE STATUS"),
HYTERA_08_ACKNOWLEDGE(Vendor.HYTERA_8, 32, "HYTERA 08 ACKNOWLEDGE"),
HYTERA_08_ANNOUNCEMENT(Vendor.HYTERA_8, 40, "HYTERA 08 ANNOUNCEMENT"),

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.type.Vendor;
import java.util.Collections;
import java.util.List;
@@ -60,6 +59,10 @@ public class UnknownCSBKMessage extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK *UNKNOWN*");
Vendor vendor = getVendor();

View File

@@ -83,6 +83,10 @@ public class Hytera08Acknowledge extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA 08 ").append(getAcknowledgeType());
sb.append(" REASON:");

View File

@@ -82,6 +82,10 @@ public class Hytera68Acknowledge extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA 68 ").append(getAcknowledgeType());
sb.append(" REASON:");

View File

@@ -85,6 +85,10 @@ public class HyteraAloha extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA ALOHA");
if(hasRadioIdentifier())

View File

@@ -77,6 +77,10 @@ public class HyteraAnnouncement extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA ANNOUNCEMENT-TYPE:").append(getAnnouncementType());
sb.append(" ").append(getSystemIdentityCode().getModel());
sb.append(" NETWORK:").append(getSystemIdentityCode().getNetwork());

View File

@@ -67,6 +67,10 @@ public class HyteraCsbko44 extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA UNKNOWN CSBKO=44");
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -75,6 +75,10 @@ public class HyteraCsbko47 extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA CSBKO=47 ######### UNKNOWN");
sb.append(" FM:").append(getSourceRadio());
sb.append(" TO:").append(getDestinationRadio());

View File

@@ -75,6 +75,10 @@ public class HyteraSmsAvailableNotification extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA SMS MESSAGE AVAILABLE FOR:").append(getDestinationRadio());
sb.append(" FM:").append(getSourceRadio());
sb.append(" UNK:").append(getUnknown());

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.ArrayList;
import java.util.List;
@@ -234,6 +233,10 @@ public class HyteraXPTSiteState extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" HYTERA XPT SITE ");
if(isAllChannelsBusy())

View File

@@ -82,6 +82,10 @@ public class CapacityMaxAloha extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CAPACITY-MAX ALOHA");
if(hasRadioIdentifier())

View File

@@ -59,6 +59,10 @@ public class CapacityPlusCSBKO_60 extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CAP+ *UNKNOWN* CSBK:60");
sb.append(" MSG:").append(getMessage().toHexString());

View File

@@ -88,6 +88,10 @@ public class CapacityPlusDataRevertWindowAnnouncement extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CAP+ ENHANCED DATA REVERT ANNOUNCEMENT");
sb.append(" WINDOW:").append(getSuperFrame()).append(".").append(getWindow());

View File

@@ -71,6 +71,10 @@ public class CapacityPlusDataRevertWindowGrant extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
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

@@ -40,8 +40,7 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequ
{
private static final int[] LC_START_STOP = new int[]{16, 17};
private static final int TIMESLOT = 18;
private static final int[] REST_REPEATER = new int[]{19, 20, 21, 22};
private static final int[] REST_TIMESLOT = new int[]{23};
private static final int[] REST_LSN = new int[]{19, 20, 21, 22, 23};
private static final int ASYNC = 24;
private static final int[] SITE = new int[]{25, 26, 27, 28};
private static final int[] NEIGHBOR_COUNT = new int[]{29, 30, 31};
@@ -89,6 +88,10 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequ
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CAP+ SITE:").append(getSite());
sb.append(" REST ").append(getRestChannel());
sb.append(" FL:").append(getLCSS());
@@ -274,13 +277,19 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequ
return LCSS.fromValue(getMessage().getInt(LC_START_STOP));
}
/**
* Rest LSN
* @return Logical Slot Number 1-16
*/
public int getRestLSN()
{
return getMessage().getInt(REST_LSN);
}
/**
* Rest repeater
*/
public int getRestRepeater()
{
return getMessage().getInt(REST_REPEATER) + 1;
}
public int getRestRepeater() { return (int) Math.ceil(getRestLSN() / 2.0); }
/**
* Rest timeslot
@@ -288,7 +297,7 @@ public class CapacityPlusNeighbors extends CSBKMessage implements ITimeslotFrequ
*/
public int getRestTimeslot()
{
return getMessage().getInt(REST_TIMESLOT) + 1;
return (getRestLSN() % 2 == 0) ? 2 : 1;
}
/**

View File

@@ -0,0 +1,325 @@
/*
* *****************************************************************************
* 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.motorola;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
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.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;
/**
* Capacity+ Site Status CSBKO=62 Message
*/
public class CapacityPlusSiteStatus extends CSBKMessage implements ITimeslotFrequencyReceiver
{
private static final int[] BYTE = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] TWO_BYTES = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
private static final int[] SEGMENT_INDICATOR = new int[]{16, 17};
private static final int TIMESLOT = 18;
private static final int RESERVED = 19;
private static final int[] REST_LSN = new int[]{20, 21, 22, 23};
private static final int[] LSN_VOICE_BITMAP = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
private static final int LSN_1_8_BITMAP_START = 24;
private static final int[][] VOICE_TALKGROUPS = 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},
{64, 65, 66, 67, 68, 69, 70, 71}, {72, 73, 74, 75, 76, 77, 78, 79}};
private DMRLogicalChannel mRestChannel;
private List<Identifier> mIdentifiers;
/**
* 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 CapacityPlusSiteStatus(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());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CAP+ SITE STATUS REST ").append(getRestChannel());
sb.append(" ").append(getSegmentIndicator());
sb.append(" ");
if(getSegmentIndicator().isFirst())
{
sb.append(getActivityFragments());
}
else
{
sb.append("CONTINUATION BLOCK");
}
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Indicates if this message has voice talkgroup activity.
*
* @return true if voice activity.
*/
private boolean hasVoiceTalkgroups()
{
return getSegmentIndicator().isFirst() && getMessage().getInt(LSN_VOICE_BITMAP) > 0;
}
/**
* Extracts the call activity from the first or first/last fragment.
* @return description of call activity.
*/
private String getActivityFragments()
{
StringBuilder sb = new StringBuilder();
sb.append("VOICE LSN ");
int pointer = LSN_1_8_BITMAP_START;
//Process voice LSNs 1-8
if(getMessage().getInt(BYTE, pointer) > 0)
{
int lowLsnBitmap = pointer;
pointer += 8;
for(int x = lowLsnBitmap; x < (lowLsnBitmap + 8); x++)
{
int lsn = x - lowLsnBitmap + 1;
if(getMessage().get(x))
{
if(pointer <= 72)
{
sb.append(lsn).append(":").append(getMessage().getInt(BYTE, pointer)).append(" ");
pointer += 8;
}
else
{
sb.append(lsn).append(":A ");
}
}
else
{
sb.append(lsn).append(":* ");
}
}
}
else
{
sb.append("1-8:* ");
pointer += 8;
}
//Process voice LSNs 9 - 16
if(pointer <= 72 && getMessage().getInt(BYTE, pointer) > 0)
{
int highLsnBitmap = pointer;
pointer += 8;
for(int x = highLsnBitmap; x < (highLsnBitmap + 8); x++)
{
int lsn = x - highLsnBitmap + 1 + 8;
if(getMessage().get(x))
{
if(pointer <= 72)
{
sb.append(lsn).append(":").append(getMessage().getInt(BYTE, pointer)).append(" ");
pointer += 8;
}
else
{
sb.append(lsn).append(":A ");
}
}
else
{
sb.append(lsn).append(":* ");
}
}
}
else
{
sb.append("9-16:* ");
pointer += 8;
}
//Process Data and Private Radio IDs - first bit in radio options byte is set to indicate more activity
if(pointer <= 72 && getMessage().get(pointer))
{
sb.append("DATA LSN ");
pointer += 8;
//If we have the data revert channel bitmap ...
if(pointer <= 72 && getMessage().getInt(BYTE, pointer) > 0)
{
int lowDataLsnBitmap = pointer;
pointer += 8;
for(int x = lowDataLsnBitmap; x < (lowDataLsnBitmap + 8); x++)
{
int lsn = x - lowDataLsnBitmap + 1;
if(getMessage().get(x))
{
if(pointer <= 64)
{
sb.append(lsn).append(":").append(getMessage().getInt(TWO_BYTES, pointer)).append(" ");
pointer += 16;
}
else
{
sb.append(lsn).append(":A ");
}
}
else
{
sb.append(lsn).append(":* ");
}
}
}
else
{
sb.append("1-8:- ");
}
}
return sb.toString();
}
/**
* Segment indicator for system status message values that are fragmented across multiple system status
* messages.
*/
public SegmentIndicator getSegmentIndicator()
{
return SegmentIndicator.fromValue(getMessage().getInt(SEGMENT_INDICATOR));
}
/**
* Current rest channel for this site.
*/
public DMRLogicalChannel getRestChannel()
{
if(mRestChannel == null)
{
mRestChannel = new DMRLogicalChannel(getRestRepeater(), getRestTimeslot());
}
return mRestChannel;
}
/**
* Rest LSN
*
* @return Logical Slot Number 1-16
*/
public int getRestLSN()
{
return getMessage().getInt(REST_LSN);
}
/**
* Rest Channel Repeater
*/
public int getRestRepeater()
{
return (int) Math.ceil(getRestLSN() / 2.0);
}
/**
* Rest Channel Timeslot
*
* @return 1 or 2
*/
public int getRestTimeslot()
{
return (getRestLSN() % 2 == 0) ? 2 : 1;
}
/**
* Logical slot numbers that require slot to frequency mappings.
*/
@Override
public int[] getLogicalTimeslotNumbers()
{
return getRestChannel().getLSNArray();
}
/**
* Applies logical slot number to frequency mapping.
*
* @param timeslotFrequencies that match the logical timeslots
*/
@Override
public void apply(List<TimeslotFrequency> timeslotFrequencies)
{
for(TimeslotFrequency timeslotFrequency : timeslotFrequencies)
{
if(getRestChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber())
{
getRestChannel().setTimeslotFrequency(timeslotFrequency);
}
}
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getRestChannel());
}
return mIdentifiers;
}
}

View File

@@ -1,157 +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.motorola;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
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.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;
/**
* Capacity+ System Status CSBKO=62 Message
*/
public class CapacityPlusSystemStatus extends CSBKMessage implements ITimeslotFrequencyReceiver
{
private static final int[] FRAGMENT_INDICATOR = new int[]{16, 17};
// private static final int TIMESLOT = 18;
private static final int[] REST_REPEATER = new int[]{19, 20, 21, 22};
private static final int[] REST_TIMESLOT = new int[]{23};
private DMRLogicalChannel mRestChannel;
private List<Identifier> mIdentifiers;
/**
* 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 CapacityPlusSystemStatus(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(" CSBK CAP+ SYSTEM STATUS ").append(getFragmentIndicator());
sb.append(" REST ").append(getRestChannel());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
/**
* Fragment indicator for system status message values that are fragmented across multiple system status
* messages.
*/
public LCSS getFragmentIndicator()
{
return LCSS.fromValue(getMessage().getInt(FRAGMENT_INDICATOR));
}
/**
* Current rest channel for this site.
*/
public DMRLogicalChannel getRestChannel()
{
if(mRestChannel == null)
{
mRestChannel = new DMRLogicalChannel(getRestRepeater(), getRestTimeslot());
}
return mRestChannel;
}
/**
* Rest Channel Repeater
*/
public int getRestRepeater()
{
return getMessage().getInt(REST_REPEATER) + 1;
}
/**
* Rest Channel Timeslot
* @return 1 or 2
*/
public int getRestTimeslot()
{
return getMessage().getInt(REST_TIMESLOT) + 1;
}
/**
* Logical slot numbers that require slot to frequency mappings.
*/
@Override
public int[] getLogicalTimeslotNumbers()
{
return getRestChannel().getLSNArray();
}
/**
* Applies logical slot number to frequency mapping.
*
* @param timeslotFrequencies that match the logical timeslots
*/
@Override
public void apply(List<TimeslotFrequency> timeslotFrequencies)
{
for(TimeslotFrequency timeslotFrequency : timeslotFrequencies)
{
if(getRestChannel().getLogicalSlotNumber() == timeslotFrequency.getNumber())
{
getRestChannel().setTimeslotFrequency(timeslotFrequency);
}
}
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getRestChannel());
}
return mIdentifiers;
}
}

View File

@@ -71,6 +71,10 @@ public class ConnectPlusCSBKO_16 extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ CSBKO:16 FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());

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.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;
@@ -77,6 +76,10 @@ public class ConnectPlusDataChannelGrant extends CSBKMessage implements ITimeslo
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK ").append(getVendor());
sb.append(" DATA CHANNEL GRANT TO:").append(getTargetRadio());
sb.append(" ").append(getChannel());

View File

@@ -70,6 +70,10 @@ public class ConnectPlusDataRevertWindowAnnouncement extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ ENHANCED DATA REVERT CHANNEL ANNOUNCE WINDOW:");
sb.append(getSuperFrame()).append(".").append(getWindow());
if(hasTargetRadio())

View File

@@ -71,6 +71,10 @@ public class ConnectPlusDataRevertWindowGrant extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
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());

View File

@@ -69,6 +69,10 @@ public class ConnectPlusNeighborReport extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ NEIGHBORS:");
List<SiteIdentifier> neighbors = getNeighbors();

View File

@@ -77,6 +77,10 @@ public class ConnectPlusOTAAnnouncement extends CSBKMessage implements ITimeslot
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ ANNOUNCE OTA ").append(getMessageType());
sb.append(" VER:").append(getMessageVersion());
sb.append(" AVAILABLE ON ").append(getDataChannel());

View File

@@ -71,6 +71,10 @@ public class ConnectPlusRegistrationRequest extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ REGISTRATION REQUEST FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());

View File

@@ -71,6 +71,10 @@ public class ConnectPlusRegistrationResponse extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ REGISTRATION RESPONSE FM:").append(getSourceRadio());
sb.append(" TO:").append(getTargetRadio());
sb.append(" UNK:").append(getUnknown());

View File

@@ -73,6 +73,10 @@ public class ConnectPlusTalkgroupAffiliation extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ AFFILIATE RADIO:").append(getRadio());
sb.append(" TO TALKGROUP:").append(getTalkgroup());
sb.append(" UNK:").append(getUnknown());

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;
@@ -76,6 +75,10 @@ public class ConnectPlusTerminateChannelGrant extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK ").append(getVendor());
sb.append(" TERMINATE CHANNEL GRANT TO:").append(getTargetRadio());
sb.append(" U1:").append(getUnknownField1());

View File

@@ -79,6 +79,10 @@ public class ConnectPlusVoiceChannelUser extends CSBKMessage implements ITimeslo
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK CON+ VOICE CHANNEL USER FM:").append(getRadio());
sb.append(" TO:").append(getTalkgroup());
sb.append(" ").append(getChannel());

View File

@@ -0,0 +1,92 @@
/*
* *****************************************************************************
* 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.motorola;
/**
* Used by Cap+ channel status message to indicate if the channel status payload is a single fragment, or a
* multi-sequence fragment.
*/
public enum SegmentIndicator
{
/**
* Continuation Segment
*/
CONTINUATION_SEGMENT("[--]"),
/**
* Last Segment in a multi-segment sequence
*/
LAST_SEGMENT("[-L]"),
/**
* First Segment in a multi-segment sequence
*/
FIRST_SEGMENT("[F-]"),
/**
* Single Segment.
*/
SINGLE_SEGMENT("[FL]"),
/**
* Unknow Segment
*/
UNKNOWN("[**]");
private String mLabel;
/**
* Constructs an instance
* @param label to display
*/
SegmentIndicator(String label)
{
mLabel = label;
}
/**
* Lookup a segment indicator from the value.
* @param value to lookup
* @return segment indicator (0-3) or UNKNOWN.
*/
public static SegmentIndicator fromValue(int value)
{
if(0 <= value && value < 4)
{
return SegmentIndicator.values()[value];
}
return UNKNOWN;
}
/**
* Indicates if this is the first in a multi-segment sequence or if this is a single-segment.
*/
public boolean isFirst()
{
return this.equals(FIRST_SEGMENT) || this.equals(SINGLE_SEGMENT);
}
@Override
public String toString()
{
return mLabel;
}
}

View File

@@ -82,6 +82,10 @@ public class Aloha extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" ALOHA");
if(hasRadioIdentifier())

View File

@@ -109,6 +109,11 @@ public class Clear extends CSBKMessage implements ITimeslotFrequencyReceiver
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

View File

@@ -119,6 +119,10 @@ public class MoveTSCC extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" MOVE TRUNK CONTROL CHANNEL ").append(getChannel());
if(hasRadioIdentifier())

View File

@@ -75,6 +75,10 @@ public class Preamble extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK PREAMBLE FM:").append(getSourceAddress());
sb.append(" TO:").append(getTargetAddress());
sb.append(isCSBKPreamble() ? " CSBK" : " DATA");

View File

@@ -77,6 +77,11 @@ public class Protect extends CSBKMessage
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

View File

@@ -80,6 +80,10 @@ public class Acknowledge extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" ").append(getAcknowledgeType());
sb.append(" REASON:").append(getReason());
sb.append(" FM:").append(getSourceRadio());

View File

@@ -65,6 +65,11 @@ public class AuthenticateRegisterRadioCheck extends Ahoy
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

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,7 +26,6 @@ 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 java.util.ArrayList;
import java.util.List;
@@ -65,6 +64,11 @@ public class CancelCall extends Ahoy
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

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
@@ -26,7 +26,6 @@ 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 java.util.ArrayList;
import java.util.List;
@@ -65,6 +64,11 @@ public class ServiceRadioCheck extends Ahoy
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

View File

@@ -65,6 +65,11 @@ public class StunReviveKill extends Ahoy
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

View File

@@ -65,6 +65,11 @@ public class UnknownAhoy extends Ahoy
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEncrypted())
{
sb.append(" ENCRYPTED");

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
@@ -27,7 +27,6 @@ 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.AnnouncementType;
import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode;
import java.util.ArrayList;
import java.util.List;
@@ -73,6 +72,10 @@ public class Announcement extends CSBKMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" ANNOUNCEMENT ").append(getAnnouncementType());
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
@@ -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.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -88,6 +87,11 @@ public class BroadcastTalkgroupVoiceChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

View File

@@ -86,6 +86,11 @@ public class DuplexPrivateDataChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

View File

@@ -86,6 +86,11 @@ public class DuplexPrivateVoiceChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

View File

@@ -86,6 +86,11 @@ public class PrivateDataChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

View File

@@ -85,6 +85,11 @@ public class PrivateVoiceChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

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.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -89,6 +88,11 @@ public class TalkgroupDataChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

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.mbc.MBCContinuationBlock;
import java.util.ArrayList;
import java.util.List;
@@ -88,6 +87,11 @@ public class TalkgroupVoiceChannelGrant extends ChannelGrant
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(isEmergency())
{
sb.append(" EMERGENCY");

View File

@@ -67,6 +67,11 @@ public class MBCHeader extends CSBKMessage
StringBuilder sb = new StringBuilder();
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(!isValid())
{
sb.append(" [CRC ERROR]");

View File

@@ -46,6 +46,12 @@ public enum LCOpcode
FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 0, "GROUP VOICE CHANNEL USER"),
FULL_CAPACITY_PLUS_WIDE_AREA_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 4, "WAN GROUP VOICE CHANNEL USER"),
//Cap+ opcodes from https://forums.radioreference.com/threads/understanding-capacity-plus-trunking-some-more.452566/
//FLCO 0: Group Call Maintenance
//FLCO 3: Private Call Maintenance (TermLC)
//FLCO 4: Group Call Grant
//FLCO 7: Private Call Grant
//FLCO 35: Private Call Maintenance (EMB)
FULL_HYTERA_GROUP_VOICE_CHANNEL_USER(Vendor.HYTERA_68, true, 0, "HYTERA GROUP VOICE CHANNEL USER"),
FULL_HYTERA_UNIT_TO_UNIT_VOICE_CHANNEL_USER(Vendor.HYTERA_68, true, 3, "HYTERA UNIT-TO-UNIT VOICE CHANNEL USER"),

View File

@@ -38,8 +38,9 @@ public class CapacityPlusWideAreaVoiceChannelUser extends CapacityPlusVoiceChann
{
private static final int[] UNKNOWN_1 = new int[]{24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] GROUP_ADDRESS = new int[]{40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] REST_REPEATER = new int[]{51, 52, 53, 54};
private static final int[] REST_TIMESLOT = new int[]{55};
//private static final int[] REST_REPEATER = new int[]{51, 52, 53, 54};
//private static final int[] REST_TIMESLOT = new int[]{55};
private static final int[] REST_LSN = new int[]{52, 53, 54, 55};
private static final int[] SOURCE_ADDRESS = new int[]{56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71};
private static final int[] UNKNOWN_3 = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
@@ -126,21 +127,27 @@ public class CapacityPlusWideAreaVoiceChannelUser extends CapacityPlusVoiceChann
return mRestChannel;
}
/**
* Rest LSN
* @return Logical Slot Number 1-16
*/
public int getRestLSN()
{
return getMessage().getInt(REST_LSN);
}
/**
* Rest channel timeslot
*/
public int getRestTimeslot()
{
return getMessage().getInt(REST_TIMESLOT) + 1;
return (getRestLSN() % 2 == 0) ? 2 : 1;
}
/**
* Rest channel repeater number
*/
public int getRestRepeater()
{
return getMessage().getInt(REST_REPEATER) + 1;
}
public int getRestRepeater() { return (int) Math.ceil(getRestLSN() / 2.0); }
/**
* Indicates if this message has a reset channel defined.

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.channel.DMRLogicalChannel;
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.DMRSite;
import java.util.ArrayList;
import java.util.List;
@@ -35,8 +34,9 @@ import java.util.List;
public class CapacityPlusRestChannel extends ShortLCMessage implements ITimeslotFrequencyReceiver
{
private static final int[] UNKNOWN = new int[]{12, 13, 14};
private static final int[] REST_REPEATER = new int[]{15, 16, 17, 18};
private static final int[] REST_TIMESLOT = new int[]{19};
//private static final int[] REST_REPEATER = new int[]{15, 16, 17, 18};
private static final int[] REST_LSN = new int[]{15, 16, 17, 18, 19};
//private static final int[] REST_TIMESLOT = new int[]{19};
private static final int[] SITE = new int[]{20, 21, 22, 23, 24};
private static final int[] UNKNOWN_2 = new int[]{25, 26, 27};
@@ -63,7 +63,7 @@ public class CapacityPlusRestChannel extends ShortLCMessage implements ITimeslot
sb.append("[CRC ERROR] ");
}
sb.append("SLC MOTOROLA CAP+ SITE:").append(getSite());
sb.append(" REST CHANNEL:").append(getRestChannel());
sb.append(" REST:").append(getRestChannel());
sb.append(" MSG:").append(getMessage().toHexString());
return sb.toString();
}
@@ -94,20 +94,26 @@ public class CapacityPlusRestChannel extends ShortLCMessage implements ITimeslot
return mRestChannel;
}
/**
* Rest LSN
* @return Logical Slot Number 1-16
*/
public int getRestLSN()
{
return getMessage().getInt(REST_LSN);
}
/**
* Rest repeater
*/
public int getRestRepeater()
{
return getMessage().getInt(REST_REPEATER) + 1;
}
public int getRestRepeater() { return (int) Math.ceil(getRestLSN() / 2.0); }
/**
* Rest timeslot
*/
public int getRestTimeslot()
{
return getMessage().getInt(REST_TIMESLOT) + 1;
return (getRestLSN() % 2 == 0) ? 2 : 1;
}
/**

View File

@@ -51,6 +51,11 @@ public class MBCContinuationBlock extends DataMessage
StringBuilder sb = new StringBuilder();
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
if(!isValid())
{
sb.append(" [CRC ERROR]");

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,7 +23,6 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
import io.github.dsheirer.module.decode.dmr.message.data.header.MBCHeader;
import io.github.dsheirer.module.decode.dmr.message.type.Vendor;
import java.util.Collections;
import java.util.List;
@@ -54,6 +53,10 @@ public class UnknownMultiCSBK extends MultiCSBK
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" CSBK *UNKNOWN*");
Vendor vendor = getVendor();

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
@@ -29,7 +29,6 @@ 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 io.github.dsheirer.module.decode.dmr.message.type.ServiceType;
import java.util.ArrayList;
import java.util.List;
@@ -80,6 +79,11 @@ public class USBData extends DataMessage
}
sb.append("CC:").append(getSlotType().getColorCode());
if(hasRAS())
{
sb.append(" RAS:").append(getBPTCReservedBits());
}
sb.append(" USB DATA BLOCK");
sb.append(" TO:").append(getTargetRadio());
sb.append(" SERVICE:").append(getServiceType());