mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
#2039 P25P2 bogus talkgroup and radio values showing in calls. Corrects message length error that was causing mis-aligned mac message parsing that contained bad/errant talkgroup and radio values to be included in the call metadata. Adds support for a new Moto feature discovery that appears to be announcing talkgroup member radios that are active on/tuned to the traffic channel. (#2042)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -188,7 +188,7 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
private final PatchGroupManager mPatchGroupManager = new PatchGroupManager();
|
||||
private final P25P1NetworkConfigurationMonitor mNetworkConfigurationMonitor;
|
||||
private final Listener<ChannelEvent> mChannelEventListener;
|
||||
private P25TrafficChannelManager mTrafficChannelManager;
|
||||
private final P25TrafficChannelManager mTrafficChannelManager;
|
||||
private ServiceOptions mCurrentServiceOptions;
|
||||
|
||||
/**
|
||||
|
||||
@@ -465,9 +465,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
|
||||
case PHASE1_90_GROUP_REGROUP_VOICE_CHANNEL_USER_ABBREVIATED:
|
||||
if(mac instanceof GroupRegroupVoiceChannelUserAbbreviated gr)
|
||||
{
|
||||
mPatchGroupManager.addPatchGroup(gr.getPatchgroup(), message.getTimestamp());
|
||||
if(gr.hasPatchgroup())
|
||||
{
|
||||
mPatchGroupManager.addPatchGroup(gr.getPatchgroup(), message.getTimestamp());
|
||||
}
|
||||
|
||||
if(gr.hasPatchgroup() || gr.hasRadio())
|
||||
{
|
||||
processChannelUser(message, mac);
|
||||
}
|
||||
}
|
||||
processChannelUser(message, mac);
|
||||
break;
|
||||
|
||||
/**
|
||||
|
||||
@@ -129,6 +129,9 @@ import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harri
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris.L3HarrisUnknownOpcode143;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris.UnknownOpcode136;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaAcknowledgeResponse;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaActiveGroupRadiosOpcode130_x82;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaActiveGroupRadiosOpcode143_x8F;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaActiveGroupRadiosOpcode191_xBF;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaDenyResponse;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupAddCommand;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.motorola.MotorolaGroupRegroupChannelGrantExplicit;
|
||||
@@ -578,12 +581,18 @@ public class MacMessageFactory
|
||||
return new MotorolaGroupRegroupVoiceChannelUserAbbreviated(message, offset);
|
||||
case MOTOROLA_81_GROUP_REGROUP_ADD:
|
||||
return new MotorolaGroupRegroupAddCommand(message, offset);
|
||||
case MOTOROLA_82_ACTIVE_GROUP_RADIOS_130:
|
||||
return new MotorolaActiveGroupRadiosOpcode130_x82(message, offset);
|
||||
case MOTOROLA_83_GROUP_REGROUP_VOICE_CHANNEL_UPDATE:
|
||||
return new MotorolaGroupRegroupVoiceChannelUpdate(message, offset);
|
||||
case MOTOROLA_84_GROUP_REGROUP_EXTENDED_FUNCTION_COMMAND:
|
||||
return new MotorolaGroupRegroupExtendedFunctionCommand(message, offset);
|
||||
case MOTOROLA_89_GROUP_REGROUP_DELETE:
|
||||
return new MotorolaGroupRegroupDeleteCommand(message, offset);
|
||||
case MOTOROLA_8F_ACTIVE_GROUP_RADIOS_143:
|
||||
return new MotorolaActiveGroupRadiosOpcode143_x8F(message, offset);
|
||||
case MOTOROLA_BF_ACTIVE_GROUP_RADIOS_191:
|
||||
return new MotorolaActiveGroupRadiosOpcode191_xBF(message, offset);
|
||||
case MOTOROLA_91_TALKER_ALIAS_HEADER:
|
||||
return new MotorolaTalkerAliasHeader(message, offset);
|
||||
case MOTOROLA_95_TALKER_ALIAS_DATA_BLOCK:
|
||||
|
||||
@@ -115,12 +115,15 @@ public enum MacOpcode
|
||||
|
||||
MOTOROLA_80_GROUP_REGROUP_VOICE_CHANNEL_USER_ABBREVIATED(Vendor.MOTOROLA, 128, "MOTOROLA GROUP REGROUP VOICE CHANNEL USER ABBREVIATED", 8),
|
||||
MOTOROLA_81_GROUP_REGROUP_ADD(Vendor.MOTOROLA, 129, "MOTOROLA GROUP REGROUP ADD", 17),
|
||||
MOTOROLA_83_GROUP_REGROUP_VOICE_CHANNEL_UPDATE(Vendor.MOTOROLA, 131, "MOTOROLA INDIVIDUAL REGROUP ACTIVE", 8),
|
||||
MOTOROLA_82_ACTIVE_GROUP_RADIOS_130(Vendor.MOTOROLA, 130, "MOTOROLA ACTIVE GROUP RADIOS 130", Integer.MIN_VALUE),
|
||||
MOTOROLA_83_GROUP_REGROUP_VOICE_CHANNEL_UPDATE(Vendor.MOTOROLA, 131, "MOTOROLA INDIVIDUAL REGROUP ACTIVE", 7),
|
||||
MOTOROLA_84_GROUP_REGROUP_EXTENDED_FUNCTION_COMMAND(Vendor.MOTOROLA, 133, "MOTOROLA GROUP REGROUP", 11),
|
||||
MOTOROLA_89_GROUP_REGROUP_DELETE(Vendor.MOTOROLA, 137, "MOTOROLA GROUP REGROUP DELETE", 17),
|
||||
MOTOROLA_8F_ACTIVE_GROUP_RADIOS_143(Vendor.MOTOROLA, 143, "MOTOROLA ACTIVE GROUP RADIOS 143", Integer.MIN_VALUE),
|
||||
MOTOROLA_BF_ACTIVE_GROUP_RADIOS_191(Vendor.MOTOROLA, 191, "MOTOROLA ACTIVE GROUP RADIOS 191", Integer.MIN_VALUE),
|
||||
//Opcode 144 uses STANDARD vendor ID for some reason.
|
||||
MOTOROLA_91_TALKER_ALIAS_HEADER(Vendor.MOTOROLA, 145, "MOTOROLA GROUP REGROUP UNKNOWN", 17),
|
||||
MOTOROLA_95_TALKER_ALIAS_DATA_BLOCK(Vendor.MOTOROLA, 149, "MOTOROLA UNKNOWN 149", 17),
|
||||
MOTOROLA_91_TALKER_ALIAS_HEADER(Vendor.MOTOROLA, 145, "MOTOROLA TALKER ALIAS HEADER", 17),
|
||||
MOTOROLA_95_TALKER_ALIAS_DATA_BLOCK(Vendor.MOTOROLA, 149, "MOTOROLA TALKER ALIAS DATA BLOCK", 17),
|
||||
MOTOROLA_A0_GROUP_REGROUP_VOICE_CHANNEL_USER_EXTENDED(Vendor.MOTOROLA, 160, "MOTOROLA GROUP REGROUP VOICE CHANNEL USER EXTENDED", 16),
|
||||
MOTOROLA_A3_GROUP_REGROUP_CHANNEL_GRANT_IMPLICIT(Vendor.MOTOROLA, 163, "MOTOROLA GROUP REGROUP CHANNEL GRANT IMPLICIT", 11),
|
||||
MOTOROLA_A4_GROUP_REGROUP_CHANNEL_GRANT_EXPLICIT(Vendor.MOTOROLA, 164, "MOTOROLA GROUP REGROUP CHANNEL GRANT EXPLICIT", 13),
|
||||
|
||||
@@ -29,7 +29,6 @@ import io.github.dsheirer.module.decode.p25.identifier.patch.APCO25PatchGroup;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.reference.ServiceOptions;
|
||||
import io.github.dsheirer.module.decode.p25.reference.VoiceServiceOptions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -64,11 +63,7 @@ public class GroupRegroupVoiceChannelUserAbbreviated extends MacStructure implem
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("GROUP REGROUP VOICE CHANNEL USER ABBREVIATED");
|
||||
sb.append(" TALKGROUP:").append(getPatchgroup());
|
||||
if(hasRadio())
|
||||
{
|
||||
sb.append(" TALKER RADIO:").append(getRadio());
|
||||
}
|
||||
|
||||
sb.append(" TALKER RADIO:").append(getRadio());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -91,6 +86,14 @@ public class GroupRegroupVoiceChannelUserAbbreviated extends MacStructure implem
|
||||
return mPatchgroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message has a non-zero patch group identifier.
|
||||
*/
|
||||
public boolean hasPatchgroup()
|
||||
{
|
||||
return getInt(SUPERGROUP) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Talker radio identifier.
|
||||
*/
|
||||
@@ -118,7 +121,11 @@ public class GroupRegroupVoiceChannelUserAbbreviated extends MacStructure implem
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getPatchgroup());
|
||||
|
||||
if(hasPatchgroup())
|
||||
{
|
||||
mIdentifiers.add(getPatchgroup());
|
||||
}
|
||||
|
||||
if(hasRadio())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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.p25.phase2.message.mac.structure.motorola;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructureVendor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Motorola Group Radios Active Feature. Observed on NM-DTRS Phase 2 network. My theory is that this lists the
|
||||
* subset of talkgroup member radios that are currently active on the traffic channel.
|
||||
*
|
||||
* Opcodes 130 and 143 are almost the same, except 143 is mostly transmitted during an ACTIVE call state and 130 is
|
||||
* mostly transmitted during the HANGTIME state. I say mostly, because there are examples where both opcodes are seen
|
||||
* in both channel states.
|
||||
*
|
||||
* Curiously: opcode 130 is in the middle of the message number sequence where GROUP REGROUP messages are allocated.
|
||||
*
|
||||
* Opcode 143 includes an additional 1-byte status field that seems to contain a 2-bit sequence counter that mostly
|
||||
* cycles through all values, but sometimes it skips values or just toggles between two of the observed values.
|
||||
* Examples: 0x00, 0x40, 0x80, 0xC0
|
||||
* There doesn't seem to be any correlation between the value of this nibble and the radio IDs being transmitted.
|
||||
*
|
||||
* The lower nibble of this 1-byte field seems to have an additional flag bit that is sometimes transmitted:
|
||||
* Examples: 0x08, 0x88
|
||||
*
|
||||
* Both opcode 130 and 143 employ up to 2x 1-byte fields that always transmit a 0x09 value. The second field is only
|
||||
* present when the message is sending 3 or 4 radio IDs.
|
||||
*
|
||||
* It seems that the complete list of active member radios is transmitted in a fixed sequence with zero to four of those
|
||||
* radios transmitted per message. The messages are transmitted when the talkgroup is first allocated to the channel
|
||||
* and eventually the 130/143 messages stop listing radios and the empty 143 and the 191 messages are intermittently
|
||||
* sent. Occasionally there are radios listed in the message with all zeros values.
|
||||
*
|
||||
* Opcode 143 is transmitted with just the status byte and no radio IDs after the full sequence of radios has been
|
||||
* sent and are no longer being sent.
|
||||
*
|
||||
* I'm not sure what Opcode 191 indicates. It's always the same (3-bytes) value: BF9003. It may be some kind of
|
||||
* feature marker to let the listening radios/devices know that the feature is active on that traffic channel.
|
||||
*
|
||||
* These three opcodes seem to be used for this feature.
|
||||
* Opcode 130/x82 - talkgroup member radios currently active - mostly transmitted during HANGTIME
|
||||
* Opcode 143/x8F - talkgroup member radios currently active - mostly transmitted during ACTIVE call state
|
||||
* Opcode 191/xBF - transmitted continuously while channel is allocated to a talkgroup - fixed value: 0xBF9003
|
||||
*/
|
||||
public class MotorolaActiveGroupRadiosOpcode130_x82 extends MacStructureVendor
|
||||
{
|
||||
private static final IntField SEPARATOR_1 = IntField.length8(OCTET_4_BIT_24); //Value is always 0x09
|
||||
private static final IntField RADIO_1 = IntField.length24(OCTET_5_BIT_32);
|
||||
private static final IntField RADIO_2 = IntField.length24(OCTET_8_BIT_56);
|
||||
private static final IntField SEPARATOR_2 = IntField.length8(OCTET_11_BIT_80); //Value is always 0x09
|
||||
private static final IntField RADIO_3 = IntField.length24(OCTET_12_BIT_88);
|
||||
private static final IntField RADIO_4 = IntField.length24(OCTET_15_BIT_112);
|
||||
private RadioIdentifier mRadio1;
|
||||
private RadioIdentifier mRadio2;
|
||||
private RadioIdentifier mRadio3;
|
||||
private RadioIdentifier mRadio4;
|
||||
|
||||
/**
|
||||
* Constructs the message
|
||||
*
|
||||
* @param message containing the message bits
|
||||
* @param offset into the message for this structure
|
||||
*/
|
||||
public MotorolaActiveGroupRadiosOpcode130_x82(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
super(message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Textual representation of this message
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("MOTOROLA 130 ACTIVE GROUP RADIOS: ");
|
||||
sb.append(getRadio1());
|
||||
|
||||
if(hasRadio2())
|
||||
{
|
||||
sb.append(", ").append(getRadio2());
|
||||
|
||||
if(hasRadio3())
|
||||
{
|
||||
sb.append(", ").append(getRadio3());
|
||||
|
||||
if(hasRadio4())
|
||||
{
|
||||
sb.append(", ").append(getRadio4());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CorrectedBinaryMessage cbm = getMessage().getSubMessage(getOffset(), getOffset() + (getLength() * 8));
|
||||
sb.append(" MSG:").append(cbm.toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio1()
|
||||
{
|
||||
if(mRadio1 == null && hasRadio1())
|
||||
{
|
||||
mRadio1 = APCO25RadioIdentifier.createAny(getInt(RADIO_1));
|
||||
}
|
||||
|
||||
return mRadio1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio2()
|
||||
{
|
||||
if(mRadio2 == null && hasRadio2())
|
||||
{
|
||||
mRadio2 = APCO25RadioIdentifier.createAny(getInt(RADIO_2));
|
||||
}
|
||||
|
||||
return mRadio2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio3()
|
||||
{
|
||||
if(mRadio3 == null && hasRadio3())
|
||||
{
|
||||
mRadio3 = APCO25RadioIdentifier.createAny(getInt(RADIO_3));
|
||||
}
|
||||
|
||||
return mRadio3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio4()
|
||||
{
|
||||
if(mRadio4 == null && hasRadio4())
|
||||
{
|
||||
mRadio4 = APCO25RadioIdentifier.createAny(getInt(RADIO_4));
|
||||
}
|
||||
|
||||
return mRadio4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio1()
|
||||
{
|
||||
return getLength() > 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio2()
|
||||
{
|
||||
return getLength() > 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio3()
|
||||
{
|
||||
return getLength() > 11;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio4()
|
||||
{
|
||||
return getLength() > 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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.p25.phase2.message.mac.structure.motorola;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructureVendor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Motorola Group Radios Active Feature. Observed on NM-DTRS Phase 2 network. My theory is that this lists the
|
||||
* subset of talkgroup member radios that are currently active on the traffic channel.
|
||||
*
|
||||
* Opcodes 130 and 143 are almost the same, except 143 is mostly transmitted during an ACTIVE call state and 130 is
|
||||
* mostly transmitted during the HANGTIME state. I say mostly, because there are examples where both opcodes are seen
|
||||
* in both channel states.
|
||||
*
|
||||
* Curiously: opcode 130 is in the middle of the message number sequence where GROUP REGROUP messages are allocated.
|
||||
*
|
||||
* Opcode 143 includes an additional 1-byte status field that seems to contain a 2-bit sequence counter that mostly
|
||||
* cycles through all values, but sometimes it skips values or just toggles between two of the observed values.
|
||||
* Examples: 0x00, 0x40, 0x80, 0xC0
|
||||
* There doesn't seem to be any correlation between the value of this nibble and the radio IDs being transmitted.
|
||||
*
|
||||
* The lower nibble of this 1-byte field seems to have an additional flag bit that is sometimes transmitted:
|
||||
* Examples: 0x08, 0x88
|
||||
*
|
||||
* Both opcode 130 and 143 employ up to 2x 1-byte fields that always transmit a 0x09 value. The second field is only
|
||||
* present when the message is sending 3 or 4 radio IDs.
|
||||
*
|
||||
* It seems that the complete list of active member radios is transmitted in a fixed sequence with zero to four of those
|
||||
* radios transmitted per message. The messages are transmitted when the talkgroup is first allocated to the channel
|
||||
* and eventually the 130/143 messages stop listing radios and the empty 143 and the 191 messages are intermittently
|
||||
* sent. Occasionally there are radios listed in the message with all zeros values.
|
||||
*
|
||||
* Opcode 143 is transmitted with just the status byte and no radio IDs after the full sequence of radios has been
|
||||
* sent and are no longer being sent.
|
||||
*
|
||||
* I'm not sure what Opcode 191 indicates. It's always the same (3-bytes) value: BF9003. It may be some kind of
|
||||
* feature marker to let the listening radios/devices know that the feature is active on that traffic channel.
|
||||
*
|
||||
* These three opcodes seem to be used for this feature.
|
||||
* Opcode 130/x82 - talkgroup member radios currently active - mostly transmitted during HANGTIME
|
||||
* Opcode 143/x8F - talkgroup member radios currently active - mostly transmitted during ACTIVE call state
|
||||
* Opcode 191/xBF - transmitted continuously while channel is allocated to a talkgroup - fixed value: 0xBF9003
|
||||
*/
|
||||
public class MotorolaActiveGroupRadiosOpcode143_x8F extends MacStructureVendor
|
||||
{
|
||||
private static final IntField STATUS = IntField.length8(OCTET_4_BIT_24);
|
||||
private static final IntField SEPARATOR_1 = IntField.length8(OCTET_5_BIT_32);
|
||||
private static final IntField RADIO_1 = IntField.length24(OCTET_6_BIT_40);
|
||||
private static final IntField RADIO_2 = IntField.length24(OCTET_9_BIT_64);
|
||||
private static final IntField SEPARATOR_2 = IntField.length8(OCTET_12_BIT_88);
|
||||
private static final IntField RADIO_3 = IntField.length24(OCTET_13_BIT_96);
|
||||
private static final IntField RADIO_4 = IntField.length24(OCTET_16_BIT_120);
|
||||
private RadioIdentifier mRadio1;
|
||||
private RadioIdentifier mRadio2;
|
||||
private RadioIdentifier mRadio3;
|
||||
private RadioIdentifier mRadio4;
|
||||
|
||||
/**
|
||||
* Constructs the message
|
||||
*
|
||||
* @param message containing the message bits
|
||||
* @param offset into the message for this structure
|
||||
*/
|
||||
public MotorolaActiveGroupRadiosOpcode143_x8F(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
super(message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Textual representation of this message
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("MOTOROLA 143 ACTIVE GROUP RADIOS: ");
|
||||
|
||||
if(hasRadio1())
|
||||
{
|
||||
sb.append(getRadio1());
|
||||
|
||||
if(hasRadio2())
|
||||
{
|
||||
sb.append(", ").append(getRadio2());
|
||||
|
||||
if(hasRadio3())
|
||||
{
|
||||
sb.append(", ").append(getRadio3());
|
||||
|
||||
if(hasRadio4())
|
||||
{
|
||||
sb.append(", ").append(getRadio4());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("NONE");
|
||||
}
|
||||
|
||||
sb.append(" STATUS:").append(getStatusCode());
|
||||
|
||||
CorrectedBinaryMessage cbm = getMessage().getSubMessage(getOffset(), getOffset() + (getLength() * 8));
|
||||
sb.append(" MSG:").append(cbm.toHexString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown status byte. First two bits seem to be a cycle counter 0x00, 0x40, 0x80, 0xC0. Second nibble, high
|
||||
* bit observed to be set sometimes: 0x08, 0x88
|
||||
* @return code value in hex.
|
||||
*/
|
||||
public String getStatusCode()
|
||||
{
|
||||
return Integer.toHexString(getInt(STATUS)).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio1()
|
||||
{
|
||||
if(mRadio1 == null && hasRadio1())
|
||||
{
|
||||
mRadio1 = APCO25RadioIdentifier.createAny(getInt(RADIO_1));
|
||||
}
|
||||
|
||||
return mRadio1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio2()
|
||||
{
|
||||
if(mRadio2 == null && hasRadio2())
|
||||
{
|
||||
mRadio2 = APCO25RadioIdentifier.createAny(getInt(RADIO_2));
|
||||
}
|
||||
|
||||
return mRadio2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio3()
|
||||
{
|
||||
if(mRadio3 == null && hasRadio3())
|
||||
{
|
||||
mRadio3 = APCO25RadioIdentifier.createAny(getInt(RADIO_3));
|
||||
}
|
||||
|
||||
return mRadio3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier or null if the message doesn't contain the value.
|
||||
*/
|
||||
public RadioIdentifier getRadio4()
|
||||
{
|
||||
if(mRadio4 == null && hasRadio4())
|
||||
{
|
||||
mRadio4 = APCO25RadioIdentifier.createAny(getInt(RADIO_4));
|
||||
}
|
||||
|
||||
return mRadio4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio1()
|
||||
{
|
||||
return getLength() > 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio2()
|
||||
{
|
||||
return getLength() > 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio3()
|
||||
{
|
||||
return getLength() > 11;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this message contains the radio value.
|
||||
*/
|
||||
public boolean hasRadio4()
|
||||
{
|
||||
return getLength() > 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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.p25.phase2.message.mac.structure.motorola;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructureVendor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Motorola Group Radios Active Feature. Observed on NM-DTRS Phase 2 network. My theory is that this lists the
|
||||
* subset of talkgroup member radios that are currently active on the traffic channel.
|
||||
*
|
||||
* Opcodes 130 and 143 are almost the same, except 143 is mostly transmitted during an ACTIVE call state and 130 is
|
||||
* mostly transmitted during the HANGTIME state. I say mostly, because there are examples where both opcodes are seen
|
||||
* in both channel states.
|
||||
*
|
||||
* Curiously: opcode 130 is in the middle of the message number sequence where GROUP REGROUP messages are allocated.
|
||||
*
|
||||
* Opcode 143 includes an additional 1-byte status field that seems to contain a 2-bit sequence counter that mostly
|
||||
* cycles through all values, but sometimes it skips values or just toggles between two of the observed values.
|
||||
* Examples: 0x00, 0x40, 0x80, 0xC0
|
||||
* There doesn't seem to be any correlation between the value of this nibble and the radio IDs being transmitted.
|
||||
*
|
||||
* The lower nibble of this 1-byte field seems to have an additional flag bit that is sometimes transmitted:
|
||||
* Examples: 0x08, 0x48, 0x88
|
||||
*
|
||||
* Both opcode 130 and 143 employ up to 2x 1-byte fields that always transmit a 0x09 value. The second field is only
|
||||
* present when the message is sending 3 or 4 radio IDs.
|
||||
*
|
||||
* It seems that the complete list of active member radios is transmitted in a fixed sequence with zero to four of those
|
||||
* radios transmitted per message. The messages are transmitted when the talkgroup is first allocated to the channel
|
||||
* and eventually the 130/143 messages stop listing radios and the empty 143 and the 191 messages are intermittently
|
||||
* sent. Occasionally there are radios listed in the message with all zeros values.
|
||||
*
|
||||
* Opcode 143 is transmitted with just the status byte and no radio IDs after the full sequence of radios has been
|
||||
* sent and are no longer being sent.
|
||||
*
|
||||
* I'm not sure what Opcode 191 indicates. It's always the same (3-bytes) value: BF9003. It may be some kind of
|
||||
* feature marker to let the listening radios/devices know that the feature is active on that traffic channel.
|
||||
*
|
||||
* These three opcodes seem to be used for this feature.
|
||||
* Opcode 130/x82 - talkgroup member radios currently active - mostly transmitted during HANGTIME
|
||||
* Opcode 143/x8F - talkgroup member radios currently active - mostly transmitted during ACTIVE call state
|
||||
* Opcode 191/xBF - transmitted continuously while channel is allocated to a talkgroup - fixed value: 0xBF9003
|
||||
*/
|
||||
public class MotorolaActiveGroupRadiosOpcode191_xBF extends MacStructureVendor
|
||||
{
|
||||
/**
|
||||
* Constructs the message
|
||||
*
|
||||
* @param message containing the message bits
|
||||
* @param offset into the message for this structure
|
||||
*/
|
||||
public MotorolaActiveGroupRadiosOpcode191_xBF(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
super(message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Textual representation of this message
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
CorrectedBinaryMessage cbm = getMessage().getSubMessage(getOffset(), getOffset() + (getLength() * 8));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("MOTOROLA 191 ACTIVE GROUP RADIOS-FEATURE ACTIVE MSG:").append(cbm.toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user