#866 Cellocator Packet Support (#875)

Resolves #866 Implements cellocator packet support.
This commit is contained in:
Denny Sheirer
2020-07-25 07:55:12 -04:00
committed by GitHub
parent 0f9d1c0792
commit 7962758e05
30 changed files with 2137 additions and 26 deletions

View File

@@ -20,6 +20,9 @@
package io.github.dsheirer.module.decode.ip;
import io.github.dsheirer.identifier.Identifier;
import java.util.List;
/**
* Data packet interface
*/
@@ -28,16 +31,21 @@ public interface IPacket
/**
* Header for the data packet.
*/
public IHeader getHeader();
IHeader getHeader();
/**
* Optional payload for the packet
* @return null if there is no payload
*/
public IPacket getPayload();
IPacket getPayload();
/**
* Indicates if this packet contains an optional payload
*/
public boolean hasPayload();
boolean hasPayload();
/**
* List of identifiers for this packet and any child payload packets.
*/
List<Identifier> getIdentifiers();
}

View File

@@ -22,6 +22,8 @@ package io.github.dsheirer.module.decode.ip;
import io.github.dsheirer.bits.BinaryMessage;
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.ipv4.IPV4Header;
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
@@ -91,6 +93,13 @@ public class PacketMessageFactory
{
switch(destinationPort)
{
case 231:
//Cellocator
if(MCGPHeader.isCellocatorMessage(binaryMessage, offset))
{
return MCGPMessageFactory.create(binaryMessage, offset);
}
break;
case 4001: //Location Service
break;
case 4004: //XCMP Service

View File

@@ -21,7 +21,10 @@
package io.github.dsheirer.module.decode.ip;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketHeader;
import java.util.Collections;
import java.util.List;
public class UnknownCompressedHeaderPacket implements IPacket
{
@@ -87,4 +90,10 @@ public class UnknownCompressedHeaderPacket implements IPacket
{
return false;
}
@Override
public List<Identifier> getIdentifiers()
{
return Collections.emptyList();
}
}

View File

@@ -21,6 +21,9 @@
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;
public class UnknownPacket implements IPacket
{
@@ -78,4 +81,10 @@ public class UnknownPacket implements IPacket
{
return false;
}
@Override
public List<Identifier> getIdentifiers()
{
return Collections.emptyList();
}
}

View File

@@ -21,8 +21,11 @@
package io.github.dsheirer.module.decode.ip.ars;
import io.github.dsheirer.bits.BinaryMessage;
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.Collections;
import java.util.List;
public class ARSPacket extends Packet
{
@@ -64,4 +67,10 @@ public class ARSPacket extends Packet
//There are no payloads for ARS packets ... the header parses all of the information.
return null;
}
@Override
public List<Identifier> getIdentifiers()
{
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,141 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* Controller (Inbound) Acknowledge Packet
*/
public class AcknowledgeMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMAND_NUMERATOR = new int[]{32, 33, 34, 35, 36, 37, 38, 39};
//This field is (possibly) byte reversed (ie big endian)
private static final int[] AUTHENTICATION_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61,
62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] ACTION_CODE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
//This field is byte reversed (ie big endian)
private static final int[] MAIN_ACKNOWLEDGE_NUMBER = new int[]{88, 89, 90, 91, 92, 93, 94, 95, 80, 81, 82, 83, 84,
85, 86, 87};
//This field is byte reversed (ie big endian)
private static final int[] SECONDARY_ACKNOWLEDGE_NUMBER = new int[]{104, 105, 106, 107, 108, 109, 110, 111, 96, 97,
98, 99, 100, 101, 102, 103};
private static final int[] RESERVED = new int[]{112, 113, 114, 115, 116, 117, 118, 119};
private static final int[] YEAR = new int[]{121, 122, 123, 124, 125, 126, 127};
private static final int[] MONTH = new int[]{133, 134, 135, 120};
private static final int[] DAY = new int[]{128, 129, 130, 131, 132};
private static final int[] RESERVED_2 = new int[]{152, 153, 154, 155, 156, 157, 158}; //Always 0x40
private static final int[] HOUR = new int[]{139, 140, 141, 142, 143};
private static final int[] MINUTE = new int[]{149, 150, 151, 136, 137, 138};
private static final int[] SECOND = new int[]{159, 144, 145, 146, 147, 148};
private CellocatorRadioIdentifier mTargetRadioId;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public AcknowledgeMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
public CellocatorRadioIdentifier getRadioId()
{
if(mTargetRadioId == null)
{
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
}
return mTargetRadioId;
}
public MCGPMessageType getAcknowledgedCommand()
{
int command = getMessage().getInt(COMMAND_NUMERATOR, getOffset());
return MCGPMessageType.getOutboundMessageType(command);
}
public String getAuthenticationCode()
{
return Integer.toHexString(getMessage().getInt(AUTHENTICATION_CODE, getOffset())).toUpperCase();
}
/**
* Always set to zero for an acknowledge message
*/
public int getActionCode()
{
return getMessage().getInt(ACTION_CODE, getOffset());
}
public int getMainAcknowledgeNumber()
{
return getMessage().getInt(MAIN_ACKNOWLEDGE_NUMBER, getOffset());
}
/**
* Currently not used and set to zero.
*/
public int getSecondaryAcknowledgeNumber()
{
return getMessage().getInt(SECONDARY_ACKNOWLEDGE_NUMBER, getOffset());
}
public long getTimestamp()
{
Calendar calendar = new GregorianCalendar();
calendar.clear();
int year = 2000 + getMessage().getInt(YEAR, getOffset());
int month = getMessage().getInt(MONTH, getOffset()) - 1; //Calendar month is zero based
int day = getMessage().getInt(DAY, getOffset());
int hour = getMessage().getInt(HOUR, getOffset());
int minute = getMessage().getInt(MINUTE, getOffset());
int second = getMessage().getInt(SECOND, getOffset());
calendar.set(year, month, day, hour, minute, second);
return calendar.getTimeInMillis();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:");
sb.append(getRadioId());
sb.append(" ACKNOWLEDGE:").append(getAcknowledgedCommand());
sb.append(" MESSAGE NUMBER:").append(getMainAcknowledgeNumber());
sb.append(" TIME:").append(new Date(getTimestamp()));
sb.append(" AUTH:").append(getAuthenticationCode());
return sb.toString();
}
}

View File

@@ -0,0 +1,68 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.protocol.Protocol;
/**
* Cellocator Radio Identifier
*/
public class CellocatorRadioIdentifier extends RadioIdentifier
{
public CellocatorRadioIdentifier(Integer value, Role role)
{
super(value, role);
}
@Override
public Protocol getProtocol()
{
return Protocol.CELLOCATOR;
}
/**
* Creates a TO radio identifier
*/
public static CellocatorRadioIdentifier createTo(int radioId)
{
return new CellocatorRadioIdentifier(radioId, Role.TO);
}
/**
* Creates a FROM radio identifier
*/
public static CellocatorRadioIdentifier createFrom(int radioId)
{
return new CellocatorRadioIdentifier(radioId, Role.FROM);
}
/**
* Creates an ANY radio identifier
*/
public static CellocatorRadioIdentifier createAny(int radioId)
{
return new CellocatorRadioIdentifier(radioId, Role.ANY);
}
}

View File

@@ -0,0 +1,109 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import java.util.Map;
import java.util.TreeMap;
/**
* Cellocator - Commands Enumeration
*/
public enum Command
{
IMMEDIATE_STATUS_REQUEST(0x0, "IMMEDIATE STATUS AND LOCATION"),
UNIT_STATUS_CHANGE(0x2, "CHANGE UNIT STATUS"),
OUTPUT_STATE_CHANGE(0x3, "CHANGE OUTPUT STATE"),
DISABLE_ACTIVE_TRANSMISSIONS(0x4, "DISABLE ACTIVE TRANSMISSIONS"),
TRACKING_CONTROL(0x5, "TRACKING CONTROL"),
ALARM_CADENCE_CONTROL(0x6, "ALARM CADENCE CONTROL"),
COMMENCE_GRADUAL_ENGINE_STOP(0x7, "COMMENCE GRADUAL ENGINE STOP"),
INITIATE_CSD_SESSION(0xC, "INITIATE CSD SESSION"),
ERASE_TRACKING_LOG(0xD, "ERASE TRACKING LOG"),
RESET_GPS_RECEIVER(0xE, "RESET GPS RECEIVER"),
SEQUENCE_DETECTION_LEARN(0xF, "SEQUENCE DETECTION LEARN"),
FORCE_GPS_ENERGIZING(0x10, "FORCE GPS ACTIVATION"),
CONNECT_TO_SERVER(0x12, "CONNECT TO SERVER"),
MANUFACTURER_RESERVED(0x13, "MANUFACTURER RESERVED"),
CALIBRATE_FREQUENCY_COUNTERS(0x14, "CALIBRATE FREQUENCY COUNTERS"),
CONTROL_COM_PORT_TRANSPARENT_MODE(0x15, "CONTROL COM PORT TRANSPARENT MODE"),
QUERY_CONNECTED_TRAILER_ID(0x16, "QUERY CONNECTED TRAILER ID"),
CAMERA_SUPPORT(0x18, "CAMERA SUPPORT"),
NANO_WAKEUP_AND_STATUS_REQUEST(0x19, "NANO WAKEUP AND STATUS REQUEST"),
ENABLE_MODEM_FOTA_SESSION(0x1A, "ENABLE MODEM FOTA SESSION"),
PHSN_CONTROL(0x1B, "PHSN CONTROL"),
CALL_CONTROL(0x1C, "CALL SHARING 2 CONTROL"),
CELLO_TRACK_T(0x1D, "CELLO TRACK T"),
CALIBRATE_CAN_GPS_SPEED(0x1F, "CALIBRATE CAN GPS SPEED"),
UNKNOWN(-1, "UNKNOWN COMMAND");
private int mValue;
private String mLabel;
/**
* Constructs an instance
* @param value of the command
* @param label for pretty printing
*/
Command(int value, String label)
{
mValue = value;
mLabel = label;
}
/**
* Value of the command
*/
public int getValue()
{
return mValue;
}
private static Map<Integer,Command> LOOKUP_MAP = new TreeMap<>();
static
{
for(Command command: Command.values())
{
LOOKUP_MAP.put(command.getValue(), command);
}
}
/**
* Lookup a command from its value
* @param value of command to lookup
* @return command or UNKNOWN
*/
public static Command fromValue(int value)
{
if(LOOKUP_MAP.containsKey(value))
{
return LOOKUP_MAP.get(value);
}
return UNKNOWN;
}
@Override
public String toString()
{
return mLabel;
}
}

View File

@@ -0,0 +1,111 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
/**
* Communication Control Field parser
*/
public class CommunicationControl
{
public enum Initiative {ACTIVE, RESPONSE};
public enum SpeedType {MOMENTARY, MAX};
private static final int MASK_CAN_ORIGINATED_ODOMETER = 0x8000;
private static final int MASK_CAN_ORIGINATED_SPEED = 0x4000;
private static final int MASK_MULTI_PURPOSE_FIELD_CONTENT = 0x3000;
private static final int MASK_CR200_CR300_MESSAGE_SOURCE = 0x800;
private static final int MASK_GARMIN_CONNECTED = 0x400;
private static final int MASK_GARMIN_ENABLED = 0x200;
private static final int MASK_CR200_CR300_MESSAGE_INITIATIVE = 0x100;
private static final int MASK_CR200_CR300_GSM_HIBERNATION_INDICATOR = 0x80;
private static final int MASK_MOMENTARY_OR_MAX_SPEED_VALUE = 0x40;
private static final int MASK_BUSINESS_OR_PRIVATE_MODE = 0x20;
private static final int MASK_FIRMWARE_SUB_VERSION = 0x1F;
private int mValue;
/**
* Constructs an instance
*
* @param value of the 16-bit field
*/
public CommunicationControl(int value)
{
mValue = value;
}
/**
* Indicates if the odometer value originated from the vehicle CAN bus
*/
public boolean isCANOriginatedOdometer()
{
return isSet(MASK_CAN_ORIGINATED_ODOMETER);
}
/**
* Indicates if the speed value originated from the vehicle CAN bus
*/
public boolean isCANOriginatedSpeed()
{
return isSet(MASK_CAN_ORIGINATED_SPEED);
}
/**
* Indicates if the Garmin GPS is connected to the unit
*/
public boolean isGarminConnected()
{
return isSet(MASK_GARMIN_CONNECTED);
}
/**
* Indicates if the Garmin GPS is enabled
*/
public boolean isGarminEnabled()
{
return isSet(MASK_GARMIN_ENABLED);
}
/**
* Indicates if the message was initiated by the unit or if the message is a response to a request.
*/
public Initiative getMessageSource()
{
return isSet(MASK_CR200_CR300_MESSAGE_INITIATIVE) ? Initiative.RESPONSE : Initiative.ACTIVE;
}
/**
* Indicates the context of the speed value contained in the message, momentary or max recorded from last event.
*/
public SpeedType getSpeedType()
{
return isSet(MASK_MOMENTARY_OR_MAX_SPEED_VALUE) ? SpeedType.MAX : SpeedType.MOMENTARY;
}
/**
* Indicates if the masked value is set for this field
* @param mask to check
* @return true if all set bits of the mask value are also set in the field bits
*/
private boolean isSet(int mask)
{
return (mValue & mask) == mask;
}
}

View File

@@ -0,0 +1,135 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Unit (Outbound) data being forwarded over the wireless interface to a central application
*/
public class ForwardedDataMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMAND_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
private static final int[] FORWARDED_MESSAGE_NUMBER = new int[]{88, 89, 90, 91, 92, 93, 94, 95};
private static final int[] FRAGMENT_CONTROL = new int[]{96, 97, 98, 99, 100, 101, 102, 103};
private static final int[] PAYLOAD_LENGTH = new int[]{112, 113, 114, 115, 116, 117, 118, 119, 104, 105, 106, 107,
108, 109, 110, 111};
private static final int PAYLOAD_START = 120;
private CellocatorRadioIdentifier mTargetRadioId;
private FragmentControl mFragmentControl;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public ForwardedDataMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
/**
* Target radio ID to receive the forwarded data
*/
public CellocatorRadioIdentifier getRadioId()
{
if(mTargetRadioId == null)
{
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
}
return mTargetRadioId;
}
/**
* Message fragment control structure
*/
public FragmentControl getFragmentControl()
{
if(mFragmentControl == null)
{
mFragmentControl = new FragmentControl(getMessage().getInt(FRAGMENT_CONTROL, getOffset()));
}
return mFragmentControl;
}
/**
* This message number
*/
public int getMessageNumerator()
{
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
}
/**
* Message number for the forwarded message
*/
public int getForwardedMessageNumerator()
{
return getMessage().getInt(FORWARDED_MESSAGE_NUMBER, getOffset());
}
/**
* Length of the payload in bytes
*/
private int getPayloadLength()
{
return getMessage().getInt(PAYLOAD_LENGTH, getOffset());
}
/**
* Hexadecimal representation of the payload data.
*/
public String getPayloadData()
{
int payloadEnd = PAYLOAD_START + (getPayloadLength() * 8);
return getMessage().get(PAYLOAD_START, payloadEnd).toHexString();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR TO RADIO:");
sb.append(getRadioId());
sb.append(" MESSAGE NUMBER:").append(getMessageNumerator());
if(getHeader().getMessageType() == MCGPMessageType.OUTBOUND_FORWARDED_REALTIME_DATA)
{
sb.append(" FORWARDED SERIAL PORT DATA");
}
else
{
sb.append(" FORWARDED LOGGED DATA");
}
sb.append(" MESSAGE NUMBER:").append(getForwardedMessageNumerator());
sb.append(" ").append(getFragmentControl());
return sb.toString();
}
}

View File

@@ -0,0 +1,70 @@
package io.github.dsheirer.module.decode.ip.cellocator;
/**
* Provides fragment information for messages that are too long and have to be fragmented.
*/
public class FragmentControl
{
public enum Origination{UNIT,APPLICATION};
private static final int BIT_MASK_ORIGINATION = 0x80;
private static final int BIT_MASK_LAST_FRAGMENT = 0x40;
private static final int BIT_MASK_FRAGMENT_INDEX = 0x3F;
private int mValue;
/**
* Constructs an instance
* @param value of the fragment control field byte
*/
public FragmentControl(int value)
{
mValue = value;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(getOrigination().name());
sb.append(" ORIGINATED FRAGMENT #").append(getFragmentIndex());
if(isLastFragment())
{
sb.append(" FINAL");
}
return sb.toString();
}
/**
* Indicates the origination of the message fragment
* @return origination, unit or application
*/
public Origination getOrigination()
{
return isSet(BIT_MASK_ORIGINATION) ? Origination.UNIT : Origination.APPLICATION;
}
/**
* Indicates if this is the last fragment
*/
public boolean isLastFragment()
{
return isSet(BIT_MASK_LAST_FRAGMENT);
}
/**
* Fragment index for this fragment, 0-31
*/
public int getFragmentIndex()
{
return (mValue & BIT_MASK_FRAGMENT_INDEX);
}
/**
* Indicates if the bits that are set in the bit mask are also set in the value field
*/
private boolean isSet(int bitMask)
{
return (mValue & bitMask) == bitMask;
}
}

View File

@@ -0,0 +1,93 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Controller (Inbound) Generic Command
*/
public class GenericCommandMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMAND_NUMERATOR = new int[]{32, 33, 34, 35, 36, 37, 38, 39};
//This field is (possibly) byte reversed (ie big endian)
private static final int[] AUTHENTICATION_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61,
62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] COMMAND_CODE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
private CellocatorRadioIdentifier mTargetRadioId;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public GenericCommandMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
public CellocatorRadioIdentifier getRadioId()
{
if(mTargetRadioId == null)
{
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
}
return mTargetRadioId;
}
public int getCommandNumerator()
{
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
}
public String getAuthenticationCode()
{
return Integer.toHexString(getMessage().getInt(AUTHENTICATION_CODE, getOffset())).toUpperCase();
}
public int getCommandCode()
{
return getMessage().getInt(COMMAND_CODE, getOffset());
}
public Command getCommand()
{
return Command.fromValue(getCommandCode());
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:");
sb.append(getRadioId());
sb.append(" REQUEST:").append(getCommand());
sb.append(" SEQUENCE:").append(getCommandNumerator());
sb.append(" AUTH:").append(getAuthenticationCode());
return sb.toString();
}
}

View File

@@ -0,0 +1,237 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.apache.commons.math3.util.FastMath;
/**
* Unit (Outbound) Status and Location
*/
public class LocationStatusMessage extends MCGPPacket
{
private static final int[] SOURCE_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMUNICATION_CONTROL = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47};
private static final int[] MESSAGE_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
private static final int[] HARDWARE_VERSION = new int[]{56, 57, 58, 59, 60, 61, 62, 63};
private static final int[] SOFTWARE_VERSION = new int[]{64, 65, 66, 67, 68, 69, 70, 71};
private static final int[] PROTOCOL_VERSION = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
private static final int[] UNIT_STATUS = new int[]{80, 81, 82, 83};
private static final int[] CURRENT_GSM_OPERATOR = new int[]{84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 152,
153, 154, 155, 156, 157, 158, 159};
private static final int[] TRANSMISSION_REASON_SPECIFIC_DATA = new int[]{96, 97, 98, 99, 100, 101, 102, 103};
private static final int[] TRANSMISSION_REASON = new int[]{104, 105, 106, 107, 108, 109, 110, 111};
private static final int[] UNIT_MODE_OF_OPERATION = new int[]{112, 113, 114, 115, 116, 117, 118, 119}; //Byte 20
private static final int[] UNIT_IO_STATUS = new int[]{120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151};
private static final int[] ANALOG_INPUT_1 = new int[]{160, 161, 162, 163, 164, 165, 166, 167};
private static final int[] ANALOG_INPUT_2 = new int[]{168, 169, 170, 171, 172, 173, 174, 175};
private static final int[] ANALOG_INPUT_3 = new int[]{176, 177, 178, 179, 180, 181, 182, 183};
private static final int[] ANALOG_INPUT_4 = new int[]{184, 185, 186, 187, 188, 189, 190, 191};
private static final int[] MILEAGE_COUNTER = new int[]{192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215};
private static final int[] MULTI_PURPOSE_FIELD = new int[]{216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263};
private static final int[] LAST_GPS_FIX = new int[]{264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
277, 278, 279};
private static final int[] LOCATION_STATUS = new int[]{280, 281, 282, 283, 284, 285, 286, 287};
private static final int[] GPS_MODE_1 = new int[]{288, 289, 290, 291, 292, 293, 294, 295};
private static final int[] GPS_MODE_2 = new int[]{296, 297, 298, 299, 300, 301, 302, 303};
private static final int[] GPS_SATELLITES_USED = new int[]{304, 305, 306, 307, 308, 309, 310, 311};
//Original signed 32-bit big endian field rearranged in little endian byte format
private static final int[] LONGITUDE = new int[]{336, 337, 338, 339, 340, 341, 342, 343, 328, 329, 330, 331, 332,
333, 334, 335, 320, 321, 322, 323, 324, 325, 326, 327, 312, 313, 314, 315, 316, 317, 318, 319};
//Original signed 32-bit big endian field rearranged in little endian byte format
private static final int[] LATITUDE = new int[]{368, 369, 370, 371, 372, 373, 374, 375, 360, 361, 362, 363, 364,
365, 366, 367, 352, 353, 354, 355, 356, 357, 358, 359, 344, 345, 346, 347, 348, 349, 350, 351};
//Original signed 32-bit big endian field rearranged in little endian byte format
private static final int[] ALTITUDE = new int[]{400, 401, 402, 403, 404, 405, 406, 407, 392, 393, 394, 395, 396,
397, 398, 399, 384, 385, 386, 387, 388, 389, 390, 391, 376, 377, 378, 379, 380, 381, 382, 383};
//Original signed 32-bit big endian field rearranged in little endian byte format
private static final int[] GROUND_SPEED = new int[]{432, 433, 434, 435, 436, 437, 438, 439, 424, 425, 426, 427, 428,
429, 430, 431, 416, 417, 418, 419, 420, 421, 422, 423, 408, 409, 410, 411, 412, 413, 414, 415};
//Original signed 32-bit big endian field rearranged in little endian byte format
private static final int[] HEADING_TRUE = new int[]{448, 449, 450, 451, 452, 453, 454, 455, 440, 441, 442, 443,
444, 445, 446, 447};
private static final int[] UTC_TIME_SECOND = new int[]{456, 457, 458, 459, 460, 461, 462, 463};
private static final int[] UTC_TIME_MINUTE = new int[]{464, 465, 466, 467, 468, 469, 470, 471};
private static final int[] UTC_TIME_HOUR = new int[]{472, 473, 474, 475, 476, 477, 478, 479};
private static final int[] UTC_TIME_DAY = new int[]{480, 481, 482, 483, 484, 485, 486, 487};
private static final int[] UTC_TIME_MONTH = new int[]{488, 489, 490, 491, 492, 493, 494, 495};
private static final int[] UTC_TIME_YEAR = new int[]{496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507,
508, 509, 510, 511};
private CellocatorRadioIdentifier mSourceRadioId;
private CommunicationControl mCommunicationControl;
private Long mUTCTimestamp;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public LocationStatusMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
/**
* Source radio or unit ID
*/
public CellocatorRadioIdentifier getRadioId()
{
if(mSourceRadioId == null)
{
mSourceRadioId = CellocatorRadioIdentifier.createFrom(getMessage().getInt(SOURCE_UNIT_ID, getOffset()));
}
return mSourceRadioId;
}
/**
* Communication control bitmap field parser.
*/
public CommunicationControl getCommunicationControl()
{
if(mCommunicationControl == null)
{
mCommunicationControl = new CommunicationControl(getMessage().getInt(COMMUNICATION_CONTROL, getOffset()));
}
return mCommunicationControl;
}
/**
* Message number. This is a one-up sequence maintained by the source unit that is reset to zero after
* each power cycle and used to track the message through receipt.
*/
public int getMessageNumerator()
{
return getMessage().getInt(MESSAGE_NUMERATOR, getOffset());
}
/**
* Number of satellites used by the GPS for calculating the position/fix.
*/
public int getSatelliteCount()
{
return getMessage().getInt(GPS_SATELLITES_USED, getOffset());
}
/**
* Latitude in degrees decimal format for WGS-84 datum
*/
public double getLatitude()
{
int value = getMessage().getInt(LATITUDE, getOffset());
double radians = (double)value / 1E8d;
return FastMath.toDegrees(radians);
}
/**
* Longitude in degrees decimal format for WGS-84 datum
*/
public double getLongitude()
{
int value = getMessage().getInt(LONGITUDE, getOffset());
double radians = (double)value / 1E8d;
return FastMath.toDegrees(radians);
}
/**
* Altitude in meters
*/
public double getAltitude()
{
int centimeters = getMessage().getInt(ALTITUDE, getOffset());
return (double)centimeters / 1E2d;
}
/**
* Speed over ground in kph
*/
public double getSpeed()
{
int centimetersPerSecond = getMessage().getInt(GROUND_SPEED, getOffset());
double kilometersPerSecond = (double)centimetersPerSecond / 1E5d;
return kilometersPerSecond * 3600;
}
/**
* Heading relative to true North, 0-360 degrees.
*/
public double getHeading()
{
int headingThousandthsRadian = getMessage().getInt(HEADING_TRUE, getOffset());
double radians = (double)headingThousandthsRadian / 1E3d;
return FastMath.toDegrees(radians);
}
/**
* UTC timestamp for the location and status
* @return timestamp in milliseconds since epoch
*/
public long getUTCTimestamp()
{
if(mUTCTimestamp == null)
{
int year = getMessage().getInt(UTC_TIME_YEAR, getOffset());
int month = getMessage().getInt(UTC_TIME_MONTH, getOffset());
int day = getMessage().getInt(UTC_TIME_DAY, getOffset());
int hour = getMessage().getInt(UTC_TIME_HOUR, getOffset());
int minute = getMessage().getInt(UTC_TIME_MINUTE, getOffset());
int second = getMessage().getInt(UTC_TIME_SECOND, getOffset());
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.clear();
calendar.set(year, month, day, hour, minute, second);
mUTCTimestamp = calendar.getTimeInMillis();
}
return mUTCTimestamp;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:").append(getRadioId());
sb.append(" GPS LOCATION: ").append(getLatitude()).append(" ").append(getLongitude());
sb.append(" HEADING:").append(getHeading());
sb.append(" SPEED:").append(getSpeed()).append("kph");
sb.append(" AT:").append(new Date(getUTCTimestamp()));
sb.append(" MESSAGE #").append(getMessageNumerator());
return sb.toString();
}
}

View File

@@ -0,0 +1,127 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.module.decode.ip.Header;
/**
* Cellocator MCGP Header
*/
public class MCGPHeader extends Header
{
private static final int[] M_CHARACTER = {0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] C_CHARACTER = {8, 9, 10, 11, 12, 13, 14, 15};
private static final int[] G_CHARACTER = {16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] P_CHARACTER = {24, 25, 26, 27, 28, 29, 30, 31};
private static final int M_VALUE = 0x4D;
private static final int C_VALUE = 0x43;
private static final int G_VALUE = 0x47;
private static final int P_VALUE = 0x50;
private static final int[] MESSAGE_TYPE = {32, 33, 34, 35, 36, 37, 38, 39};
public static final int HEADER_LENGTH = 40;
private MCGPMessageType mMessageType;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param message containing the header
* @param offset to the header within the message
*/
public MCGPHeader(BinaryMessage message, int offset)
{
super(message, offset);
}
/**
* Performs simple check of the first 32 bits of the message looking for the
* signature 'MCGP' header for cellocator messages.
* @param message to inspect
* @param offset to the cellocator payload
* @return true if the payload starts with MCGP
*/
public static boolean isCellocatorMessage(BinaryMessage message, int offset)
{
return message.getInt(M_CHARACTER, offset) == M_VALUE &&
message.getInt(C_CHARACTER, offset) == C_VALUE &&
message.getInt(G_CHARACTER, offset) == G_VALUE &&
message.getInt(P_CHARACTER, offset) == P_VALUE;
}
@Override
public int getLength()
{
return HEADER_LENGTH;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR ").append(getMessageType());
if(getMessageType() == MCGPMessageType.UNKNOWN)
{
sb.append(" PACKET:").append(getMessage().toHexString());
}
return sb.toString();
}
/**
* Indicates the type of message
*/
public MCGPMessageType getMessageType()
{
if(mMessageType == null)
{
mMessageType = getMessageType(getMessage(), getOffset());
}
return mMessageType;
}
/**
* Sets/Overrides the message type
*/
void setMessageType(MCGPMessageType type)
{
mMessageType = type;
}
/**
* Indicates the type of MCGP message contained in the message starting at the offset index.
*/
public static MCGPMessageType getMessageType(BinaryMessage message, int offset)
{
int payloadLengthBytes = (message.size() - offset) / 8;
return MCGPMessageType.fromValue(message.getInt(MESSAGE_TYPE, offset), payloadLengthBytes);
}
/**
* Numeric value for the message type for this header
*/
public int getMessageTypeValue()
{
return getMessage().getInt(MESSAGE_TYPE, getOffset());
}
}

View File

@@ -0,0 +1,77 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Creates an MCGP packet and header for parsing the contents of the packet.
*/
public class MCGPMessageFactory
{
/**
* Creates an MCGP packet message from the binary message at the specified binary message offset.
* @param message containing transmitted bits
* @param offset into the message for the start of the MCGP payload
* @return MCGP packet message parser
*/
public static MCGPPacket create(BinaryMessage message, int offset)
{
MCGPHeader header = new MCGPHeader(message, offset);
int payloadOffset = offset + MCGPHeader.HEADER_LENGTH;
switch(header.getMessageType())
{
case INBOUND_GENERIC_COMMAND:
return new GenericCommandMessage(header, message, payloadOffset);
case INBOUND_GENERAL_ACKNOWLEDGE:
return new AcknowledgeMessage(header, message, payloadOffset);
case INBOUND_PROGRAMMING_COMMAND:
return new ProgrammingCommandMessage(header, message, payloadOffset);
case OUTBOUND_LOCATION_STATUS:
return new LocationStatusMessage(header, message, payloadOffset);
case OUTBOUND_PROGRAMMING_STATUS:
return new ProgrammingStatusMessage(header, message, payloadOffset);
case OUTBOUND_FORWARDED_REALTIME_DATA:
case OUTBOUND_FORWARDED_LOGGED_DATA_FRAGMENT:
return new ForwardedDataMessage(header, message, payloadOffset);
case INBOUND_MODULAR_REQUEST:
case OUTBOUND_MODULAR_RESPONSE:
//Both messages are variable length and use message type 9 so we have to inspect the total message
//length field for the response message which should be 0 for the request variant and non-zero for
//the response variant.
if(ModularResponseMessage.getTotalMessageLength(message, payloadOffset) > 0)
{
header.setMessageType(MCGPMessageType.OUTBOUND_MODULAR_RESPONSE);
return new ModularResponseMessage(header, message, payloadOffset);
}
else
{
return new ModularRequestMessage(header, message, payloadOffset);
}
case UNKNOWN:
default:
return new UnknownMCGPMessage(header, message, offset);
}
}
}

View File

@@ -0,0 +1,127 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import java.util.EnumSet;
/**
* Cellocator MCGP Message Types enumeration with indicated message lengths.
*
* Note: message length of -1 indicates a variable length message.
*/
public enum MCGPMessageType
{
INBOUND_GENERIC_COMMAND(0, 25 * 8, "COMMAND"),
INBOUND_PROGRAMMING_COMMAND(1, 34 * 8, "PROGRAMMING COMMAND"),
INBOUND_GENERAL_ACKNOWLEDGE(4, 28 * 8, "ACKNOWLEDGE"),
INBOUND_FORWARD_DATA_COMMAND(5, -1, "FORWARD DATA COMMAND"),
INBOUND_MODULAR_REQUEST(9, -1, "MODULAR MESSAGE REQUEST"),
OUTBOUND_LOCATION_STATUS(0, 70 * 8, "LOCATION AND STATUS"),
OUTBOUND_PROGRAMMING_STATUS(3, 31 * 8, "CONFIGURATION"),
OUTBOUND_FORWARDED_LOGGED_DATA_FRAGMENT(7, -1, "FORWARDED LOGGED DATA"),
OUTBOUND_FORWARDED_REALTIME_DATA(8, -1, "FORWARDED SERIAL_PORT DATA"),
OUTBOUND_MODULAR_RESPONSE(9, -1, "MODULAR MESSAGE RESPONSE"),
OUTBOUND_FIRMWARE_UPDATE(10,-1, "FIRMWARE UPDATE"),
UNKNOWN(-1, -1, "UNKNOWN");
private int mValue;
private int mLength;
private String mLabel;
MCGPMessageType(int value, int length, String label)
{
mValue = value;
mLength = length;
mLabel = label;
}
public static EnumSet<MCGPMessageType> INBOUND = EnumSet.range(INBOUND_GENERIC_COMMAND, INBOUND_MODULAR_REQUEST);
public static EnumSet<MCGPMessageType> OUTBOUND = EnumSet.range(OUTBOUND_LOCATION_STATUS, OUTBOUND_FIRMWARE_UPDATE);
public int getValue()
{
return mValue;
}
public int getLength()
{
return mLength;
}
/**
* Utility method to lookup an entry from a value.
*/
public static MCGPMessageType fromValue(int value, int messageByteLength)
{
switch(value)
{
case 0:
if(messageByteLength == 25)
{
return INBOUND_GENERIC_COMMAND;
}
else if(messageByteLength == 70)
{
return OUTBOUND_LOCATION_STATUS;
}
break;
case 1:
return INBOUND_PROGRAMMING_COMMAND;
case 3:
return OUTBOUND_PROGRAMMING_STATUS;
case 4:
return INBOUND_GENERAL_ACKNOWLEDGE;
case 5:
return INBOUND_FORWARD_DATA_COMMAND;
case 7:
return OUTBOUND_FORWARDED_LOGGED_DATA_FRAGMENT;
case 8:
return OUTBOUND_FORWARDED_REALTIME_DATA;
case 9:
//Defer to the message factory to detect inbound vs outbound message type
return INBOUND_MODULAR_REQUEST;
case 10:
return OUTBOUND_FIRMWARE_UPDATE;
}
return UNKNOWN;
}
public static MCGPMessageType getOutboundMessageType(int value)
{
for(MCGPMessageType type: OUTBOUND)
{
if(type.getValue() == value)
{
return type;
}
}
return UNKNOWN;
}
@Override
public String toString()
{
return mLabel;
}
}

View File

@@ -0,0 +1,79 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
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.Collections;
import java.util.List;
/**
* Cellocator MCGP Packet
*/
public abstract class MCGPPacket extends Packet
{
private MCGPHeader mHeader;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public MCGPPacket(MCGPHeader header, BinaryMessage message, int offset)
{
super(message, offset);
mHeader = header;
}
@Override
public MCGPHeader getHeader()
{
return mHeader;
}
/**
* Unit ID (to/from) for this packet
*/
protected abstract CellocatorRadioIdentifier getRadioId();
@Override
public IPacket getPayload()
{
return null;
}
@Override
public List<Identifier> getIdentifiers()
{
CellocatorRadioIdentifier radioIdentifier = getRadioId();
if(radioIdentifier != null)
{
return Collections.singletonList(radioIdentifier);
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,44 @@
package io.github.dsheirer.module.decode.ip.cellocator;
/**
* Modular message data types
*/
public enum ModularDataType
{
FIRMWARE_PLATFORM_MANIFEST(1),
CAN_DATA(2),
CAN_TRIGGER_DATA(3),
TIME_AND_LOCATION_DATA(4),
ACCELEROMETER_DATA(5),
PSP_ALARM_SYSTEM_DATA(6),
USAGE_COUNTER_DATA(7),
COMMAND_AUTHENTICATION_TABLE_DATA(8),
GSM_NEIGHBOR_LIST(9),
MAINTENANCE_SERVER_PLATFORM_MANIFEST(10),
UNKNOWN(0);
private int mValue;
ModularDataType(int value)
{
mValue = value;
}
public int getValue()
{
return mValue;
}
public static ModularDataType fromValue(int value)
{
for(ModularDataType type: ModularDataType.values())
{
if(type.getValue() == value)
{
return type;
}
}
return UNKNOWN;
}
}

View File

@@ -0,0 +1,110 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Inbound (from Controller) Modular Request Message
*/
public class ModularRequestMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMAND_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
private static final int[] PACKET_CONTROL = new int[]{88, 89, 90, 91, 92, 93, 94, 95};
private static final int[] TOTAL_MESSAGE_LENGTH = new int[]{96, 97, 98, 99, 100, 101, 102, 103};
private static final int[] SUB_DATA_1_MESSAGE_TYPE = new int[]{104, 105, 106, 107, 108, 109, 110, 111};
private CellocatorRadioIdentifier mTargetRadioId;
private PacketControl mPacketControl;
/**
* Constructs a parser for the message.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public ModularRequestMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
/**
* Static access to message total length (bytes) field.
* @param message containing modular request message
* @param offset to start of modular request message
* @return value from the total message length field
*/
public static int getTotalMessageLength(BinaryMessage message, int offset)
{
return message.getInt(TOTAL_MESSAGE_LENGTH, offset);
}
/**
* Radio/Unit identifier, either source or target as specified in the PacketControl field.
*/
public CellocatorRadioIdentifier getRadioId()
{
if(mTargetRadioId == null)
{
int radioId = getMessage().getInt(TARGET_UNIT_ID, getOffset());
mTargetRadioId = CellocatorRadioIdentifier.createTo(radioId);
}
return mTargetRadioId;
}
public PacketControl getPacketControl()
{
if(mPacketControl == null)
{
mPacketControl = new PacketControl(getMessage().getInt(PACKET_CONTROL, getOffset()));
}
return mPacketControl;
}
public int getCommandNumerator()
{
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
}
/**
* Modular data type for the first sub-chunk
*/
public ModularDataType getModularDataType()
{
return ModularDataType.fromValue(getMessage().getInt(SUB_DATA_1_MESSAGE_TYPE, getOffset()));
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:");
sb.append(getRadioId());
sb.append(" MODULAR REQUEST TYPE:").append(getModularDataType());
sb.append(" MESSAGE NUMBER:").append(getCommandNumerator());
return sb.toString();
}
}

View File

@@ -0,0 +1,139 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Modular Message
*/
public class ModularResponseMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMUNICATION_CONTROL = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47};
private static final int[] MESSAGE_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
private static final int[] PACKET_CONTROL = new int[]{56, 57, 58, 59, 60, 61, 62, 63};
private static final int[] TOTAL_MESSAGE_LENGTH = new int[]{64, 65, 66, 67, 68, 69, 70, 71};
private static final int[] SUB_DATA_1_MESSAGE_TYPE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
private CellocatorRadioIdentifier mRadioId;
private CommunicationControl mCommunicationControl;
private PacketControl mPacketControl;
/**
* Constructs a parser for the message.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public ModularResponseMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
/**
* Static access to message total length (bytes) field.
* @param message containing modular response message
* @param offset to start of modular response message
* @return value from the total message length field
*/
public static int getTotalMessageLength(BinaryMessage message, int offset)
{
return message.getInt(TOTAL_MESSAGE_LENGTH, offset);
}
/**
* Radio/Unit identifier, either source or target as specified in the PacketControl field.
*/
public CellocatorRadioIdentifier getRadioId()
{
if(mRadioId == null)
{
int radioId = getMessage().getInt(UNIT_ID, getOffset());
if(getPacketControl().getDirection() == PacketControl.Direction.FROM_UNIT)
{
mRadioId = CellocatorRadioIdentifier.createFrom(radioId);
}
else
{
mRadioId = CellocatorRadioIdentifier.createTo(radioId);
}
}
return mRadioId;
}
public PacketControl getPacketControl()
{
if(mPacketControl == null)
{
mPacketControl = new PacketControl(getMessage().getInt(PACKET_CONTROL, getOffset()));
}
return mPacketControl;
}
public CommunicationControl getCommunicationControl()
{
if(mCommunicationControl == null)
{
mCommunicationControl = new CommunicationControl(getMessage().getInt(COMMUNICATION_CONTROL, getOffset()));
}
return mCommunicationControl;
}
public int getMessageNumerator()
{
return getMessage().getInt(MESSAGE_NUMERATOR, getOffset());
}
/**
* Modular data type for the first sub-chunk
*/
public ModularDataType getModularDataType()
{
return ModularDataType.fromValue(getMessage().getInt(SUB_DATA_1_MESSAGE_TYPE, getOffset()));
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:");
if(getPacketControl().getDirection() == PacketControl.Direction.FROM_UNIT)
{
sb.append(" FROM:");
}
else
{
sb.append(" TO:");
}
sb.append(getRadioId());
sb.append(" MODULAR TYPE:").append(getModularDataType());
sb.append(" MESSAGE NUMBER:").append(getMessageNumerator());
return sb.toString();
}
}

View File

@@ -0,0 +1,60 @@
package io.github.dsheirer.module.decode.ip.cellocator;
/**
* Provides fragment information for messages that are too long and have to be fragmented.
*/
public class PacketControl
{
public enum Direction {TO_UNIT,FROM_UNIT};
private static final int BIT_MASK_DIRECTION = 0x80;
private static final int BIT_MASK_OUT_OF_SPACE = 0x40;
private int mValue;
/**
* Constructs an instance
* @param value of the fragment control field byte
*/
public PacketControl(int value)
{
mValue = value;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(" MODULAR PACKET ").append(getDirection());
if(isOutOfSpace())
{
sb.append(" (MESSAGE EXCEEDED SPACE)");
}
return sb.toString();
}
/**
* Indicates the origination of the message fragment
* @return origination, unit or application
*/
public Direction getDirection()
{
return isSet(BIT_MASK_DIRECTION) ? Direction.TO_UNIT : Direction.FROM_UNIT;
}
/**
* Indicates if the message contents exceeded the available space in this message
*/
public boolean isOutOfSpace()
{
return isSet(BIT_MASK_OUT_OF_SPACE);
}
/**
* Indicates if the bits that are set in the bit mask are also set in the value field
*/
private boolean isSet(int bitMask)
{
return (mValue & bitMask) == bitMask;
}
}

View File

@@ -0,0 +1,104 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
import org.apache.commons.lang3.StringUtils;
/**
* Controller (Inbound) Programming Command
*/
public class ProgrammingCommandMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMAND_NUMERATOR = new int[]{32, 33, 34, 35, 36, 37, 38, 39};
//This field is (possibly) byte reversed (ie big endian)
private static final int[] AUTHENTICATION_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61,
62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] BLOCK_CODE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
private static final int[] BITMASK = new int[]{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95};
private static final int DATA_BLOCK_START = 96;
private static final int DATA_BLOCK_END = 224;
private CellocatorRadioIdentifier mTargetRadioId;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public ProgrammingCommandMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
public CellocatorRadioIdentifier getRadioId()
{
if(mTargetRadioId == null)
{
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
}
return mTargetRadioId;
}
public int getCommandNumerator()
{
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
}
public String getAuthenticationCode()
{
return Integer.toHexString(getMessage().getInt(AUTHENTICATION_CODE, getOffset())).toUpperCase();
}
public int getBlockCode()
{
return getMessage().getInt(BLOCK_CODE, getOffset());
}
public String getBitMask()
{
return StringUtils.leftPad(Integer.toHexString(getMessage().getInt(BITMASK, getOffset())).toUpperCase(), 4, "0");
}
public String getDataBlock()
{
return getMessage().getSubMessage(DATA_BLOCK_START + getOffset(), DATA_BLOCK_END + getOffset()).toHexString();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:");
sb.append(getRadioId());
sb.append(" PROGRAM SEQUENCE NUMBER:").append(getCommandNumerator());
sb.append(" AUTH:").append(getAuthenticationCode());
sb.append(" BLOCK:").append(getBlockCode());
sb.append(" BITMASK:").append(getBitMask());
sb.append(" DATA:").append(getDataBlock());
return sb.toString();
}
}

View File

@@ -0,0 +1,101 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Unit (Outbound) Programming Status/Response
*/
public class ProgrammingStatusMessage extends MCGPPacket
{
//This field is byte reversed (ie big endian)
private static final int[] SOURCE_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] COMMUNICATION_CONTROL = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47};
private static final int[] MESSAGE_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
private static final int[] SPARE = new int[]{56, 57, 58, 59, 60, 61, 62, 63};
private static final int[] BLOCK_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71};
private static final int DATA_BLOCK_START = 72;
private static final int DATA_BLOCK_END = DATA_BLOCK_START + 128;
private CellocatorRadioIdentifier mSourceRadioId;
private CommunicationControl mCommunicationControl;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public ProgrammingStatusMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
public CellocatorRadioIdentifier getRadioId()
{
if(mSourceRadioId == null)
{
mSourceRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(SOURCE_UNIT_ID, getOffset()));
}
return mSourceRadioId;
}
public CommunicationControl getCommunicationControl()
{
if(mCommunicationControl == null)
{
mCommunicationControl = new CommunicationControl(getMessage().getInt(COMMUNICATION_CONTROL, getOffset()));
}
return mCommunicationControl;
}
public int getMessageNumerator()
{
return getMessage().getInt(MESSAGE_NUMERATOR, getOffset());
}
public int getBlockCode()
{
return getMessage().getInt(BLOCK_CODE, getOffset());
}
public String getDataBlock()
{
return getMessage().getSubMessage(DATA_BLOCK_START + getOffset(), DATA_BLOCK_END + getOffset()).toHexString();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR RADIO:");
sb.append(getRadioId());
sb.append(" PROGRAM MESSAGE NUMBER:").append(getMessageNumerator());
sb.append(" BLOCK:").append(getBlockCode());
sb.append(" DATA:").append(getDataBlock());
return sb.toString();
}
}

View File

@@ -0,0 +1,70 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.cellocator;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.module.decode.ip.IPacket;
import io.github.dsheirer.module.decode.ip.UnknownPacket;
/**
* MCGP Packet - Unknown Message Type
*/
public class UnknownMCGPMessage extends MCGPPacket
{
private IPacket mPayload;
/**
* Constructs a parser for a header contained within a binary message starting at the offset.
*
* @param header for this message
* @param message containing the packet
* @param offset to the packet within the message
*/
public UnknownMCGPMessage(MCGPHeader header, BinaryMessage message, int offset)
{
super(header, message, offset);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("CELLOCATOR UNKNOWN MESSAGE TYPE [");
sb.append(getHeader().getMessageTypeValue()).append("] ").append(getPayload());
return sb.toString();
}
@Override
public IPacket getPayload()
{
if(mPayload == null)
{
mPayload = new UnknownPacket(getMessage(), getOffset());
}
return mPayload;
}
@Override
protected CellocatorRadioIdentifier getRadioId()
{
return null;
}
}

View File

@@ -21,10 +21,13 @@
package io.github.dsheirer.module.decode.ip.ipv4;
import io.github.dsheirer.bits.BinaryMessage;
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.PacketMessageFactory;
import io.github.dsheirer.module.decode.ip.UnknownPacket;
import java.util.ArrayList;
import java.util.List;
/**
* IPV4 packet
@@ -33,6 +36,7 @@ public class IPV4Packet extends Packet
{
private IPV4Header mHeader;
private IPacket mPayload;
private List<Identifier> mIdentifiers;
/**
* Constructs an IPV4 packet parser
@@ -109,4 +113,23 @@ public class IPV4Packet extends Packet
return mPayload;
}
@Override
public List<Identifier> getIdentifiers()
{
if(mIdentifiers == null)
{
mIdentifiers = new ArrayList<>();
if(hasPayload())
{
mIdentifiers.addAll(getPayload().getIdentifiers());
}
mIdentifiers.add(getHeader().getToAddress());
mIdentifiers.add(getHeader().getFromAddress());
}
return mIdentifiers;
}
}

View File

@@ -21,10 +21,13 @@
package io.github.dsheirer.module.decode.ip.udp;
import io.github.dsheirer.bits.BinaryMessage;
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.PacketMessageFactory;
import io.github.dsheirer.module.decode.ip.UnknownPacket;
import java.util.Collections;
import java.util.List;
public class UDPPacket extends Packet
{
@@ -73,16 +76,30 @@ public class UDPPacket extends Packet
@Override
public IPacket getPayload()
{
if(mPayload == null && getHeader().isValid())
if(mPayload == null)
{
mPayload = PacketMessageFactory.createUDPPayload(getHeader().getDestinationPort(), getMessage(), getOffset() + getHeader().getLength());
}
else
{
//Set the offset to the message offset plus 64 bits for UDP header
mPayload = new UnknownPacket(getMessage(), getOffset() + 64);
if(getHeader().isValid())
{
mPayload = PacketMessageFactory.createUDPPayload(getHeader().getDestinationPort(), getMessage(), getOffset() + getHeader().getLength());
}
else
{
//Set the offset to the message offset plus 64 bits for UDP header
mPayload = new UnknownPacket(getMessage(), getOffset() + 64);
}
}
return mPayload;
}
@Override
public List<Identifier> getIdentifiers()
{
if(hasPayload())
{
return getPayload().getIdentifiers();
}
return Collections.emptyList();
}
}

View File

@@ -43,6 +43,7 @@ import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.ip.IPacket;
import io.github.dsheirer.module.decode.ip.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket;
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
import io.github.dsheirer.module.decode.p25.P25DecodeEvent;
@@ -935,6 +936,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ARSPacket arsPacket = (ARSPacket)udpPayload;
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
for(Identifier identifier: packet.getIdentifiers())
{
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
@@ -945,9 +950,32 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
broadcast(packetEvent);
}
else if(udpPayload instanceof MCGPPacket)
{
MCGPPacket mcgpPacket = (MCGPPacket)udpPayload;
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
for(Identifier identifier: packet.getIdentifiers())
{
ic.update(identifier);
}
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription("CELLOCATOR")
.identifiers(ic)
.details(mcgpPacket.toString() + " " + ipv4.toString())
.build();
broadcast(cellocatorEvent);
}
else
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
for(Identifier identifier: packet.getIdentifiers())
{
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())

View File

@@ -25,7 +25,6 @@ import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.id.record.Record;
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
import io.github.dsheirer.bits.BitSetFullException;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
@@ -34,7 +33,6 @@ import io.github.dsheirer.dsp.psk.pll.IPhaseLockedLoop;
import io.github.dsheirer.dsp.symbol.Dibit;
import io.github.dsheirer.dsp.symbol.ISyncDetectListener;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.IMessageProvider;
import io.github.dsheirer.message.Message;
import io.github.dsheirer.message.MessageProviderModule;
import io.github.dsheirer.message.StuffBitsMessage;
@@ -42,23 +40,17 @@ import io.github.dsheirer.message.SyncLossMessage;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.p25.audio.P25P1AudioModule;
import io.github.dsheirer.module.decode.p25.audio.P25P2AudioModule;
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
import io.github.dsheirer.module.decode.p25.phase1.message.P25MessageFactory;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessageFactory;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUSequence;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessageFactory;
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState;
import io.github.dsheirer.module.decode.p25.phase2.P25P2MessageFramer;
import io.github.dsheirer.module.decode.p25.phase2.P25P2MessageProcessor;
import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.record.AudioRecordingManager;
import io.github.dsheirer.record.binary.BinaryReader;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
import org.slf4j.Logger;
@@ -458,7 +450,7 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
public static void main(String[] args)
{
Path directory = Paths.get("/home/denny/temp/Harris P25-2 Logs/bits");
Path directory = Paths.get("/home/denny/Documents/TMR/APCO25/GPS/cnyicc_cellocator");
// Path directory = Paths.get("/media/denny/500G1EXT4/PBITRecordings");
UserPreferences userPreferences = new UserPreferences();
@@ -479,11 +471,13 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
MessageProviderModule messageProviderModule = new MessageProviderModule();
processingChain.addModule(messageProviderModule);
mLog.info("Processing Directory: " + directory.toString());
try(OutputStream logOutput = Files.newOutputStream(directory.resolve("log.txt")))
{
try
{
DirectoryStream<Path> stream = Files.newDirectoryStream(directory, "*PHASE1_Hills*TRAFFIC.bits");
DirectoryStream<Path> stream = Files.newDirectoryStream(directory, "*.bits");
stream.forEach(new Consumer<Path>()
{
@@ -520,7 +514,11 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
{
mLog.error("Error", ioe);
}
mLog.debug(message.toString());
if(message instanceof PacketMessage)
{
mLog.debug(message.toString());
}
}
}
});

View File

@@ -264,11 +264,9 @@ public class PacketMessage extends P25Message
mIdentifiers = new ArrayList<>();
mIdentifiers.add(getPDUSequence().getHeader().getLLID());
if(getPacket() instanceof IPV4Packet)
if(getPacket() != null)
{
IPV4Packet ipv4 = (IPV4Packet)getPacket();
mIdentifiers.add(ipv4.getHeader().getFromAddress());
mIdentifiers.add(ipv4.getHeader().getToAddress());
mIdentifiers.addAll(getPacket().getIdentifiers());
}
}

View File

@@ -30,6 +30,7 @@ public enum Protocol
{
APCO25("APCO-25", "APCO25PHASE1", 9600),
APCO25_PHASE2("APCO-25 P2", "APCO25PHASE2", 12000),
CELLOCATOR("CELLOCATOR", "CELLOCATOR", 0),
FLEETSYNC("Fleetsync", "FLEETSYNC", 1200),
IPV4("IPV4", "IPV4", 0),
LOJACK("LoJack", "LOJACK", 1200),