mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Resolves #866 Implements cellocator packet support.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user