From 2a45849e6dea45eaea847075bd069ce65f34c738 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sat, 27 Nov 2021 13:42:04 -0500 Subject: [PATCH] #1115 Adds more parsing updates for LRRP packets. (#1116) Co-authored-by: Dennis Sheirer --- .../dsheirer/identifier/location/Point.java | 10 +- .../module/decode/dmr/DMRMessageFramer.java | 114 ++++++++++++++++-- .../module/decode/ip/lrrp/LRRPPacket.java | 61 +++++++++- .../module/decode/ip/lrrp/LRRPPacketType.java | 32 +++-- .../{GeoFencePosition.java => Circle2d.java} | 18 +-- ...{GeoFencePosition3D.java => Circle3d.java} | 27 +++-- .../module/decode/ip/lrrp/token/Heading.java | 7 +- .../token/{Position.java => Point2d.java} | 24 ++-- .../token/{Position3D.java => Point3d.java} | 15 ++- .../decode/ip/lrrp/token/Request61.java | 60 +++++++++ .../decode/ip/lrrp/token/Request73.java | 60 +++++++++ .../decode/ip/lrrp/token/RequestedTokens.java | 99 +++++++++++++++ .../module/decode/ip/lrrp/token/Response.java | 14 +-- .../decode/ip/lrrp/token/ResponseCode.java | 2 + .../module/decode/ip/lrrp/token/Speed.java | 17 +-- .../module/decode/ip/lrrp/token/Success.java | 54 +++++++++ .../decode/ip/lrrp/token/TokenFactory.java | 30 +++-- .../decode/ip/lrrp/token/TokenType.java | 66 +++++++--- .../decode/ip/lrrp/token/TriggerDistance.java | 65 ++++++++++ .../decode/ip/lrrp/token/TriggerGpio.java | 53 ++++++++ .../decode/ip/lrrp/token/TriggerOnMove.java | 53 ++++++++ .../decode/ip/lrrp/token/TriggerPeriodic.java | 63 ++++++++++ .../decode/p25/phase1/P25P1MessageFramer.java | 25 +++- .../p25/phase1/message/tsbk/Opcode.java | 31 +++-- .../io/github/dsheirer/util/PacketUtil.java | 9 +- 25 files changed, 878 insertions(+), 131 deletions(-) rename src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/{GeoFencePosition.java => Circle2d.java} (71%) rename src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/{GeoFencePosition3D.java => Circle3d.java} (62%) rename src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/{Position.java => Point2d.java} (78%) rename src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/{Position3D.java => Point3d.java} (73%) create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request61.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request73.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Success.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerDistance.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerGpio.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerOnMove.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerPeriodic.java diff --git a/src/main/java/io/github/dsheirer/identifier/location/Point.java b/src/main/java/io/github/dsheirer/identifier/location/Point.java index 57aa1884..380bf222 100644 --- a/src/main/java/io/github/dsheirer/identifier/location/Point.java +++ b/src/main/java/io/github/dsheirer/identifier/location/Point.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2021 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 @@ -19,11 +19,15 @@ package io.github.dsheirer.identifier.location; +import java.text.DecimalFormat; + /** * Geospatial point (location) */ public class Point { + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00000"); + private double mLatitude; private double mLongitude; @@ -52,8 +56,8 @@ public class Point public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(Math.abs(mLatitude)).append(mLatitude >= 0 ? "N " : "S "); - sb.append(Math.abs(mLongitude)).append(mLongitude >= 0 ? "E" : "W"); + sb.append(DECIMAL_FORMAT.format(Math.abs(mLatitude))).append(mLatitude >= 0 ? "N " : "S "); + sb.append(DECIMAL_FORMAT.format(Math.abs(mLongitude))).append(mLongitude >= 0 ? "E" : "W"); return sb.toString(); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageFramer.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageFramer.java index 32276186..d3a1690d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageFramer.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRMessageFramer.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2021 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,10 @@ import io.github.dsheirer.module.decode.dmr.message.CACH; import io.github.dsheirer.module.decode.dmr.message.DMRBurst; import io.github.dsheirer.module.decode.dmr.message.DMRMessage; import io.github.dsheirer.module.decode.dmr.message.DMRMessageFactory; +import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage; +import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket; +import io.github.dsheirer.module.decode.ip.lrrp.token.Identity; +import io.github.dsheirer.module.decode.ip.lrrp.token.Token; import io.github.dsheirer.protocol.Protocol; import io.github.dsheirer.record.binary.BinaryReader; import io.github.dsheirer.sample.Listener; @@ -40,6 +44,11 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Consumer; /** @@ -257,11 +266,20 @@ public class DMRMessageFramer implements Listener, IDMRBurstDetectListene if(message instanceof DMRBurst && ((DMRBurst)message).hasCACH()) { CACH cach = ((DMRBurst)message).getCACH(); - mLog.info(cach.toString() + " TS:" + message.getTimeslot() + " " + message.toString()); +// mLog.info(cach.toString() + " TS:" + message.getTimeslot() + " " + message.toString()); } else { - mLog.info(" TS:" + message.getTimeslot() + " " + message.toString()); +// mLog.info(" TS:" + message.getTimeslot() + " " + message.toString()); + } + + if(message instanceof DMRPacketMessage dpm) + { + if(dpm.getPacket() instanceof LRRPPacket lrrp) + { + mLog.info("LRRP: " + lrrp.toString()); + mLog.info("MESSAGE: " + lrrp.getMessage().toHexString()); + } } // if(message instanceof UDTHeader) @@ -286,6 +304,80 @@ public class DMRMessageFramer implements Listener, IDMRBurstDetectListene } } + public static class LrrpProcessor implements Listener + { + private Map> mLrrpMap = new HashMap<>(); + + public LrrpProcessor() + { + + } + + @Override + public void receive(IMessage message) + { + if(message instanceof DMRPacketMessage dpm) + { + if(dpm.getPacket() instanceof LRRPPacket lrrp) + { + process(lrrp); + } + } + } + + public void process(LRRPPacket lrrpPacket) + { + Identity identity = getIdentity(lrrpPacket); + + if(identity != null) + { + List packets = mLrrpMap.get(identity.getID()); + + if(packets == null) + { + packets = new ArrayList<>(); + } + + packets.add(lrrpPacket); + + mLrrpMap.put(identity.getID(), packets); + } + } + + public void log() + { + mLog.info("*************** DUMPING LRRP PACKETS****************************"); + List ids = new ArrayList(mLrrpMap.keySet()); + Collections.sort(ids); + + for(Integer id: ids) + { + mLog.info(""); + mLog.info("###################### ID: " + id + " ###################################"); + List packets = mLrrpMap.get(id); + + for(LRRPPacket lrrp: packets) + { + mLog.info("PACKET: " + lrrp.toString()); + mLog.info("MESSAGE: " + lrrp.getMessage().toHexString()); + } + } + } + + private static Identity getIdentity(LRRPPacket packet) + { + for(Token token: packet.getTokens()) + { + if(token instanceof Identity) + { + return (Identity)token; + } + } + + return null; + } + } + public static void main(String[] args) { String path = "/media/denny/Lexar/Recordings/DMR/"; @@ -334,16 +426,15 @@ public class DMRMessageFramer implements Listener, IDMRBurstDetectListene // String file = path + "20200730_090634_9600BPS_DMR_Otsego_County_Road_Commission_Alpine_Center_Road_Commission.bits"; //Cap+ BP Scrambling -// String file = path + "20200829_065610_9600BPS_DMR_Albany_Medical_Center_Albany_LCN_3.bits"; - - String file = "/home/denny/SDRTrunk/recordings/20200927_054837_9600BPS_DMR_Blair_Communications_(Capacity_Plus)_Dallas_Control_9.bits"; - + String file = path + "20200829_065610_9600BPS_DMR_Albany_Medical_Center_Albany_LCN_3.bits"; +// String file = "/home/denny/SDRTrunk/recordings/20200927_054837_9600BPS_DMR_Blair_Communications_(Capacity_Plus)_Dallas_Control_9.bits"; MessageListener listener = new MessageListener(); DecodeConfigDMR config = new DecodeConfigDMR(); + LrrpProcessor lrrpProcessor = new LrrpProcessor(); - boolean multi = false; + boolean multi = true; if(multi) { @@ -360,7 +451,9 @@ public class DMRMessageFramer implements Listener, IDMRBurstDetectListene DMRMessageFramer messageFramer = new DMRMessageFramer(null); DMRMessageProcessor messageProcessor = new DMRMessageProcessor(config); messageFramer.setListener(messageProcessor); - messageProcessor.setMessageListener(listener); +// messageProcessor.setMessageListener(listener); + messageProcessor.setMessageListener(lrrpProcessor); + try(BinaryReader reader = new BinaryReader(path, 200)) { @@ -400,6 +493,9 @@ public class DMRMessageFramer implements Listener, IDMRBurstDetectListene { ioe.printStackTrace(); } + + System.out.println("Logging LRRP Packets ..."); + lrrpProcessor.log(); } else { diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacket.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacket.java index d0c1aaab..34172a8f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacket.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacket.java @@ -23,8 +23,10 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.ip.IPacket; import io.github.dsheirer.module.decode.ip.Packet; +import io.github.dsheirer.module.decode.ip.lrrp.token.RequestedTokens; import io.github.dsheirer.module.decode.ip.lrrp.token.Token; import io.github.dsheirer.module.decode.ip.lrrp.token.TokenFactory; +import io.github.dsheirer.module.decode.ip.lrrp.token.TokenType; import java.util.ArrayList; import java.util.Collections; @@ -95,6 +97,11 @@ public class LRRPPacket extends Packet return mTokens; } + private boolean isResponsePacket() + { + return getHeader().getLRRPPacketType().isResponse(); + } + private void parseTokens() { if(mTokens == null) @@ -104,13 +111,55 @@ public class LRRPPacket extends Packet int characterCount = getHeader().getPayloadLength(); int offset = getOffset() + TOKEN_START; - while(characterCount > 0 && offset < getMessage().size()) + if(isResponsePacket()) { - String tokenId = getTokenIdentifier(offset); - Token token = TokenFactory.createToken(tokenId, getMessage(), offset, characterCount); - mTokens.add(token); - characterCount -= token.getByteLength(); - offset += (8 * token.getByteLength()); + while(characterCount > 0 && offset < getMessage().size()) + { + String tokenId = getTokenIdentifier(offset); + Token token = TokenFactory.createToken(tokenId, getMessage(), offset, characterCount); + mTokens.add(token); + characterCount -= token.getByteLength(); + offset += (8 * token.getByteLength()); + } + } + else + { + String message = getMessage().getSubMessage(getOffset(), getOffset() + ((characterCount + 2) * 8)).toHexString(); + int x = 0; + + RequestedTokens requestedTokens = new RequestedTokens(); + + while(characterCount > 0 && offset < getMessage().size()) + { + String tokenId = getTokenIdentifier(offset); + TokenType tokenType = TokenType.fromValue(tokenId); + + if(tokenType.isRequestParameterToken()) + { + Token token = TokenFactory.createToken(tokenId, getMessage(), offset, characterCount); + mTokens.add(token); + characterCount -= token.getByteLength(); + offset += (8 * token.getByteLength()); + } + else + { + if(tokenType != TokenType.UNKNOWN) + { + requestedTokens.add(tokenType); + } + else + { + requestedTokens.add(tokenId); + } + characterCount -= 1; + offset += (8); + } + } + + if(requestedTokens.hasRequestedTokens()) + { + mTokens.add(requestedTokens); + } } Collections.sort(mTokens, (o1, o2) -> o1.getTokenType().compareTo(o2.getTokenType())); diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java index fd1e8dfc..1b3a1a9a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/LRRPPacketType.java @@ -24,25 +24,27 @@ package io.github.dsheirer.module.decode.ip.lrrp; */ public enum LRRPPacketType { - IMMEDIATE_LOCATION_REQUEST(0x05, "IMMEDIATE LOCATION REQUEST"), - IMMEDIATE_LOCATION_RESPONSE(0x07, "IMMEDIATE LOCATION RESPONSE"), - TRIGGERED_LOCATION_START_REQUEST(0x09, "TRIGGERED LOCATION START REQUEST"), - TRIGGERED_LOCATION_START_RESPONSE(0x0B, "TRIGGERED LOCATION START RESPONSE"), - TRIGGERED_LOCATION(0x0D, "TRIGGERED LOCATION"), - TRIGGERED_LOCATION_STOP_REQUEST(0x0F, "TRIGGERED LOCATION STOP REQUEST"), - TRIGGERED_LOCATION_STOP_RESPONSE(0x11, "TRIGGERED LOCATION STOP RESPONSE"), - PROTOCOL_VERSION_REQUEST(0x14, "PROTOCOL VERSION REQUEST"), - PROTOCOL_VERSION_RESPONSE(0x15, "PROTOCOL VERSION RESPONSE"), + IMMEDIATE_LOCATION_REQUEST(0x05, "IMMEDIATE LOCATION REQUEST", false), + IMMEDIATE_LOCATION_RESPONSE(0x07, "IMMEDIATE LOCATION RESPONSE", true), + TRIGGERED_LOCATION_START_REQUEST(0x09, "TRIGGERED LOCATION START REQUEST", false), + TRIGGERED_LOCATION_START_RESPONSE(0x0B, "TRIGGERED LOCATION START RESPONSE", true), + TRIGGERED_LOCATION(0x0D, "TRIGGERED LOCATION", true), + TRIGGERED_LOCATION_STOP_REQUEST(0x0F, "TRIGGERED LOCATION STOP REQUEST", false), + TRIGGERED_LOCATION_STOP_RESPONSE(0x11, "TRIGGERED LOCATION STOP RESPONSE", true), + PROTOCOL_VERSION_REQUEST(0x14, "PROTOCOL VERSION REQUEST", false), + PROTOCOL_VERSION_RESPONSE(0x15, "PROTOCOL VERSION RESPONSE", true), - UNKNOWN(-1, "UNKNOWN"); + UNKNOWN(-1, "UNKNOWN", false); private int mValue; private String mLabel; + private boolean mIsResponse; - LRRPPacketType(int value, String label) + LRRPPacketType(int value, String label, boolean isResponse) { mValue = value; mLabel = label; + mIsResponse = isResponse; } /** @@ -59,6 +61,14 @@ public enum LRRPPacketType return mLabel; } + /** + * Indicates if this is a response type + */ + public boolean isResponse() + { + return mIsResponse; + } + /** * Lookup the LRRP packet type from the value. * @param value to lookup diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/GeoFencePosition.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Circle2d.java similarity index 71% rename from src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/GeoFencePosition.java rename to src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Circle2d.java index c2fa043a..a68307ed 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/GeoFencePosition.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Circle2d.java @@ -21,13 +21,15 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import java.text.DecimalFormat; + /** - * LRRP Geo-Fence Position with latitude, longitude and radius + * LRRP Approximate Position with latitude, longitude and error */ -public class GeoFencePosition extends Position +public class Circle2d extends Point2d { - private static final int[] RADIUS_WHOLE = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; - private static final int[] RADIUS_FRACTIONAL = new int[]{80, 81, 82, 83, 84, 85, 86, 87}; + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00"); + private static final int[] RADIUS = new int[]{72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87}; /** * Constructs an instance of an approximate position token. @@ -35,7 +37,7 @@ public class GeoFencePosition extends Position * @param message containing the heading * @param offset to the start of the token */ - public GeoFencePosition(CorrectedBinaryMessage message, int offset) + public Circle2d(CorrectedBinaryMessage message, int offset) { super(message, offset); } @@ -43,7 +45,7 @@ public class GeoFencePosition extends Position @Override public TokenType getTokenType() { - return TokenType.POSITION_GEO_FENCE; + return TokenType.CIRCLE_2D; } /** @@ -53,13 +55,13 @@ public class GeoFencePosition extends Position */ public float getRadius() { - return getFloat(RADIUS_WHOLE, RADIUS_FRACTIONAL); + return getMessage().getInt(RADIUS, getOffset()) * HUNDREDTHS_MULTIPLIER; } @Override public String toString() { CorrectedBinaryMessage sub = getMessage().getSubMessage(getOffset(), getOffset() + 87); - return "GEO-FENCE POSITION:" + getPosition() + " RADIUS:" + getRadius() + " KM"; + return "CIRCLE 2D POSITION:" + getPosition() + " RADIUS:" + DECIMAL_FORMAT.format(getRadius()) + " MTRS"; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/GeoFencePosition3D.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Circle3d.java similarity index 62% rename from src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/GeoFencePosition3D.java rename to src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Circle3d.java index 1a0f905a..2e734a45 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/GeoFencePosition3D.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Circle3d.java @@ -21,15 +21,16 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import java.text.DecimalFormat; + /** * LRRP 3D Position with latitude, longitude, altitude and radius */ -public class GeoFencePosition3D extends GeoFencePosition +public class Circle3d extends Circle2d { - private static final int[] ALTITUDE_WHOLE = new int[]{88, 89, 90, 91, 92, 93, 94, 95}; - private static final int[] ALTITUDE_FRACTIONAL = new int[]{96, 97, 98, 99, 100, 101, 102, 103}; - private static final int[] ALTITUDE_ACCURACY_WHOLE = new int[]{104, 105, 106, 107, 108, 109, 110, 111}; - private static final int[] ALTITUDE_ACCURACY_FRACTIONAL = new int[]{112, 113, 114, 115, 116, 117, 118, 119}; + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00"); + private static final int[] ALTITUDE = new int[]{88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + private static final int[] ALTITUDE_ACCURACY = new int[]{104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; /** * Constructs an instance of a 3D position token. @@ -37,7 +38,7 @@ public class GeoFencePosition3D extends GeoFencePosition * @param message containing the heading * @param offset to the start of the token */ - public GeoFencePosition3D(CorrectedBinaryMessage message, int offset) + public Circle3d(CorrectedBinaryMessage message, int offset) { super(message, offset); } @@ -45,7 +46,7 @@ public class GeoFencePosition3D extends GeoFencePosition @Override public TokenType getTokenType() { - return TokenType.POSITION_GEO_FENCE_3D; + return TokenType.CIRCLE_3D; } /** @@ -55,7 +56,7 @@ public class GeoFencePosition3D extends GeoFencePosition */ public float getAltitude() { - return getFloat(ALTITUDE_WHOLE, ALTITUDE_FRACTIONAL); + return getMessage().getInt(ALTITUDE, getOffset()) * HUNDREDTHS_MULTIPLIER; } /** @@ -64,7 +65,7 @@ public class GeoFencePosition3D extends GeoFencePosition */ public float getAltitudeAccuracy() { - return getFloat(ALTITUDE_ACCURACY_WHOLE, ALTITUDE_ACCURACY_FRACTIONAL); + return getMessage().getInt(ALTITUDE_ACCURACY, getOffset()) * HUNDREDTHS_MULTIPLIER; } @@ -72,10 +73,10 @@ public class GeoFencePosition3D extends GeoFencePosition public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("GEO-FENCE POSITION:").append(getPosition()); - sb.append(" RADIUS:").append(getRadius()); - sb.append("KM ALTITUDE:").append(getAltitude()); - sb.append(" ALT ACCURACY:").append(getAltitudeAccuracy()); + sb.append("CIRCLE 3D POSITION:").append(getPosition()); + sb.append(" RADIUS:").append(DECIMAL_FORMAT.format(getRadius())); + sb.append("MTRS ALTITUDE:").append(DECIMAL_FORMAT.format(getAltitude())); + sb.append(" ALT ACCURACY:").append(DECIMAL_FORMAT.format(getAltitudeAccuracy())); return sb.toString(); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Heading.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Heading.java index 72b8e91c..97f567dc 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Heading.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Heading.java @@ -21,6 +21,8 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import java.text.DecimalFormat; + /** * LRRP Heading Token * @@ -29,8 +31,9 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; */ public class Heading extends Token { + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("000.0"); private static final int[] HEADING = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; - private static final float HEADING_MULTIPLIER = 360.0f / 256.0f; + private static final float HEADING_MULTIPLIER = 2.0f; /** * Constructs an instance of a heading token. @@ -60,6 +63,6 @@ public class Heading extends Token @Override public String toString() { - return "HEADING:" + getHeading(); + return "HEADING:" + DECIMAL_FORMAT.format(getHeading()); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Position.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Point2d.java similarity index 78% rename from src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Position.java rename to src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Point2d.java index 4f7018f9..ea0138a5 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Position.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Point2d.java @@ -21,24 +21,24 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import java.text.DecimalFormat; + /** * LRRP Position Token * * Start Token: 0x66 * Total Length: 9 bytes */ -public class Position extends Token +public class Point2d extends Token { - private static final int LATITUDE_HEMISPHERE_FLAG = 8; - private static final int[] LATITUDE = new int[]{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + private static final int[] LATITUDE = new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; - private static final int LONGITUDE_HEMISPHERE_FLAG = 40; - private static final int[] LONGITUDE = new int[]{41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + private static final int[] LONGITUDE = new int[]{40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; private static final double LATITUDE_MULTIPLIER = 180.0 / 4294967295.0d; private static final double LONGITUDE_MULTIPLIER = 360.0 / 4294967295.0d; - protected static final float FRACTIONAL_MULTIPLIER = 0.01f; + protected static final float HUNDREDTHS_MULTIPLIER = 0.01f; private LRRPPosition mPosition; @@ -48,7 +48,7 @@ public class Position extends Token * @param message containing the heading * @param offset to the start of the token */ - public Position(CorrectedBinaryMessage message, int offset) + public Point2d(CorrectedBinaryMessage message, int offset) { super(message, offset); } @@ -56,7 +56,7 @@ public class Position extends Token @Override public TokenType getTokenType() { - return TokenType.POSITION; + return TokenType.POINT_2D; } /** @@ -77,7 +77,7 @@ public class Position extends Token */ public double getLatitude() { - return getMessage().getInt(LATITUDE, getOffset()) * LATITUDE_MULTIPLIER * (getMessage().get(LATITUDE_HEMISPHERE_FLAG) ? -1 : 1); + return getMessage().getInt(LATITUDE, getOffset()) * LATITUDE_MULTIPLIER;// * (getMessage().get(LATITUDE_HEMISPHERE_FLAG) ? -1 : 1); } /** @@ -85,7 +85,7 @@ public class Position extends Token */ public double getLongitude() { - return getMessage().getInt(LONGITUDE, getOffset()) * LONGITUDE_MULTIPLIER * (getMessage().get(LONGITUDE_HEMISPHERE_FLAG) ? -1 : 1); + return getMessage().getInt(LONGITUDE, getOffset()) * LONGITUDE_MULTIPLIER;// * (getMessage().get(LONGITUDE_HEMISPHERE_FLAG) ? -1 : 1); } /** @@ -97,12 +97,12 @@ public class Position extends Token protected float getFloat(int[] whole, int[] fractional) { return getMessage().getInt(whole, getOffset()) + - (getMessage().getInt(fractional, getOffset()) * FRACTIONAL_MULTIPLIER); + (getMessage().getInt(fractional, getOffset()) * HUNDREDTHS_MULTIPLIER); } @Override public String toString() { - return "POSITION:" + getPosition(); + return "POINT:" + getPosition(); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Position3D.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Point3d.java similarity index 73% rename from src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Position3D.java rename to src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Point3d.java index 667cc521..eb9d98a0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Position3D.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Point3d.java @@ -24,11 +24,10 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; /** * LRRP 3D Position with latitude, longitude and altitude */ -public class Position3D extends Position +public class Point3d extends Point2d { - //Note: these may not be correct ... there may be 3 bytes available for altitude - private static final int[] ALTITUDE_WHOLE = new int[]{72, 73, 74, 75, 76, 77, 78, 79}; - private static final int[] ALTITUDE_FRACTIONAL = new int[]{80, 81, 82, 83, 84, 85, 86, 87}; + private static final int[] ALTITUDE = new int[]{72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95}; /** * Constructs an instance of an approximate position token. @@ -36,7 +35,7 @@ public class Position3D extends Position * @param message containing the heading * @param offset to the start of the token */ - public Position3D(CorrectedBinaryMessage message, int offset) + public Point3d(CorrectedBinaryMessage message, int offset) { super(message, offset); } @@ -44,7 +43,7 @@ public class Position3D extends Position @Override public TokenType getTokenType() { - return TokenType.POSITION_3D; + return TokenType.POINT_3D; } /** @@ -54,13 +53,13 @@ public class Position3D extends Position */ public float getAltitude() { - return getFloat(ALTITUDE_WHOLE, ALTITUDE_FRACTIONAL); + return getMessage().getInt(ALTITUDE, getOffset()) * HUNDREDTHS_MULTIPLIER; } @Override public String toString() { CorrectedBinaryMessage sub = getMessage().getSubMessage(getOffset(), getOffset() + 87); - return "POSITION:" + getPosition() + " ALTITUDE:" + getAltitude() + " MTRS"; + return "POINT:" + getPosition() + " ALTITUDE:" + getAltitude() + " MTRS"; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request61.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request61.java new file mode 100644 index 00000000..c7f9fa87 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request61.java @@ -0,0 +1,60 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * LRRP Unknown Request Type 61 + *

+ * Total Length: 2 bytes + */ +public class Request61 extends Token +{ + private static final int[] VALUE = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; + + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public Request61(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.REQUEST_61; + } + + public int getValue() + { + return getMessage().getInt(VALUE, getOffset()); + } + + @Override + public String toString() + { + return "REQUEST-61:" + getValue(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request73.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request73.java new file mode 100644 index 00000000..7e7c409b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Request73.java @@ -0,0 +1,60 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * LRRP Unknown Request Type 73 + * + * Total Length: 2 bytes + */ +public class Request73 extends Token +{ + private static final int[] VALUE = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; + + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public Request73(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.REQUEST_73; + } + + public int getValue() + { + return getMessage().getInt(VALUE, getOffset()); + } + + @Override + public String toString() + { + return "REQUEST-73:" + getValue(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java new file mode 100644 index 00000000..0e3c8787 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/RequestedTokens.java @@ -0,0 +1,99 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import com.google.common.base.Joiner; +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * LRRP Requested Element Token + * + * Start Token: various + * Total Length: 1 byte + */ +public class RequestedTokens extends Token +{ + private List mRequestedTokens = new ArrayList<>(); + private List mUnrecognizedRequestedTokens = new ArrayList<>(); + + /** + * Constructs an instance of a heading token. + */ + public RequestedTokens() + { + super(null, 0); + } + + @Override + public TokenType getTokenType() + { + return TokenType.REQUESTED_TOKENS; + } + + /** + * Adds the recognized requested token type + */ + public void add(TokenType tokenType) + { + mRequestedTokens.add(tokenType); + } + + public void add(String unrecognizedTokenType) + { + mUnrecognizedRequestedTokens.add(unrecognizedTokenType); + Collections.sort(mUnrecognizedRequestedTokens); + } + + /** + * Indicates if this object has requested tokens. + */ + public boolean hasRequestedTokens() + { + return !mRequestedTokens.isEmpty(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("REQUESTED TOKENS ["); + if(!mRequestedTokens.isEmpty()) + { + sb.append(Joiner.on(",").join(mRequestedTokens)); + } + + if(!mRequestedTokens.isEmpty() && !mUnrecognizedRequestedTokens.isEmpty()) + { + sb.append(","); + } + + if(!mUnrecognizedRequestedTokens.isEmpty()) + { + sb.append("UNRECOGNIZED(").append(Joiner.on(",").join(mUnrecognizedRequestedTokens)).append(")"); + } + + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Response.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Response.java index 2d37aa02..98c62e33 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Response.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Response.java @@ -29,9 +29,9 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; */ public class Response extends Token { - private static final int CODE_LENGTH_FLAG = 16; - private static final int[] ONE_BYTE_CODE = new int[]{17, 18, 19, 20, 21, 22, 23}; - private static final int[] TWO_BYTE_CODE = new int[]{17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + private static final int CODE_LENGTH_FLAG = 8; + private static final int[] ONE_BYTE_CODE = new int[]{9, 10, 11, 12, 13, 14, 15}; + private static final int[] TWO_BYTE_CODE = new int[]{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; /** * Constructs an instance of a response code token. @@ -65,11 +65,11 @@ public class Response extends Token { if(isExtendedCode()) { - return getMessage().getInt(TWO_BYTE_CODE); + return getMessage().getInt(TWO_BYTE_CODE, getOffset()); } else { - return getMessage().getInt(ONE_BYTE_CODE); + return getMessage().getInt(ONE_BYTE_CODE, getOffset()); } } @@ -94,9 +94,9 @@ public class Response extends Token if(responseCode == ResponseCode.UNKNOWN) { - return "RESPONSE CODE: UNKNOWN[" + getCodeValue() + "]"; + return "RESPONSE: UNKNOWN[" + getCodeValue() + "]"; } - return "RESPONSE CODE:" + responseCode; + return "RESPONSE:" + responseCode; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/ResponseCode.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/ResponseCode.java index 0a5640bd..3feddd3c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/ResponseCode.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/ResponseCode.java @@ -28,7 +28,9 @@ public enum ResponseCode { SUCCESS(0x0, "SUCCESS"), INVALID_COMMAND(0x0A, "INVALID COMMAND"), + UNKNOWN_ERROR_F(0x0F, "UNRECOGNIZED ERROR [0F]"), NO_GPS(0x10, "NO GPS"), + DUPLICATE_REQUEST(0x16, "DUPLICATE REQUEST"), GPS_INITIALIZING(0x200, "GPS INITIALIZING"), UNKNOWN(-1, "UNKNOWN"); diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Speed.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Speed.java index 2c96dfde..1264f876 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Speed.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Speed.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer + * Copyright (C) 2014-2021 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 @@ -21,16 +21,19 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; import io.github.dsheirer.bits.CorrectedBinaryMessage; +import java.text.DecimalFormat; + /** * LRRP Speed Token - * + *

* Start Token: 0x6C * Total Length: 3 bytes */ public class Speed extends Token { - private static final int[] UNKNOWN = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; - private static final int[] SPEED = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; + private static final int[] SPEED = new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + private static final double SPEED_MULTIPLIER = 0.01; //Units of 1/100 mph + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00"); /** * Constructs an instance of a heading token. @@ -52,14 +55,14 @@ public class Speed extends Token /** * Speed in kilometers per hour (kph) */ - public long getSpeed() + public double getSpeed() { - return getMessage().getInt(SPEED, getOffset()); + return getMessage().getInt(SPEED, getOffset()) * SPEED_MULTIPLIER; } @Override public String toString() { - return "SPEED:" + getSpeed() + " KPH"; + return "SPEED:" + DECIMAL_FORMAT.format(getSpeed()) + " MPH"; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Success.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Success.java new file mode 100644 index 00000000..d733ac38 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/Success.java @@ -0,0 +1,54 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * LRRP Success Token + * + * Start Token: 0x38 + * Total Length: 1 bytes + */ +public class Success extends Token +{ + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public Success(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.SUCCESS; + } + + @Override + public String toString() + { + return "SUCCESS!"; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenFactory.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenFactory.java index d85904f1..ae4f3d15 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenFactory.java @@ -36,24 +36,38 @@ public class TokenFactory return new Heading(message, offset); case IDENTITY: return new Identity(message, offset); - case POSITION: - return new Position(message, offset); - case POSITION_GEO_FENCE: - return new GeoFencePosition(message, offset); - case POSITION_3D: - return new Position3D(message, offset); - case POSITION_GEO_FENCE_3D: - return new GeoFencePosition3D(message, offset); + case POINT_2D: + return new Point2d(message, offset); + case CIRCLE_2D: + return new Circle2d(message, offset); + case POINT_3D: + return new Point3d(message, offset); + case CIRCLE_3D: + return new Circle3d(message, offset); case RESPONSE: return new Response(message, offset); case SPEED: return new Speed(message, offset); + case SUCCESS: + return new Success(message, offset); case TIMESTAMP: return new Timestamp(message, offset); + case TRIGGER_DISTANCE: + return new TriggerDistance(message, offset); + case TRIGGER_GPIO: + return new TriggerGpio(message, offset); + case TRIGGER_ON_MOVE: + return new TriggerOnMove(message, offset); + case TRIGGER_PERIODIC: + return new TriggerPeriodic(message, offset); case VERSION: return new Version(message, offset); case UNKNOWN_23: return new Unknown23(message, offset); + case REQUEST_61: + return new Request61(message, offset); + case REQUEST_73: + return new Request73(message, offset); case UNKNOWN: default: return new UnknownToken(message, offset, remainingCharacterCount); diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java index 63d22ef6..01e4d84a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TokenType.java @@ -19,6 +19,7 @@ package io.github.dsheirer.module.decode.ip.lrrp.token; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; @@ -30,32 +31,54 @@ import java.util.Map; */ public enum TokenType { - IDENTITY("22", -1), - UNKNOWN_23("23", 1), - TIMESTAMP("34", 5), - VERSION("36", 1), - RESPONSE("37", -1), -// SUCCESS_RESPONSE_CODE( "38", 0), //Maybe: https://github.com/pboyd04/Moto.Net/blob/master/Moto.Net/Mototrbo/LRRP/TriggeredLocationStartResponsePacket.cs - POSITION_GEO_FENCE("51", 10), - POSITION_GEO_FENCE_3D("55", 15), - HEADING("56", 1), - POSITION("66", 8), - POSITION_3D("69", 11), - SPEED("6C", 2), - UNKNOWN("0",0); + IDENTITY("22", -1, "IDENTITY"), + UNKNOWN_23("23", 1, "UNKNOWN 23"), + TRIGGER_PERIODIC("31", 1, "TRIGGER PERIODIC"), + TIMESTAMP("34", 5, "TIMESTAMP"), + VERSION("36", 1, "VERSION"), + RESPONSE("37", -1, "RESPONSE"), + SUCCESS( "38", 0, "SUCCESS"), + REQUEST_3A("3A", 0,"REQUEST-3A"), + TRIGGER_GPIO("42", 0, "TRIGGER GPIO"), + TRIGGER_DISTANCE("4A", 1, "TRIGGER DISTANCE"), //Guess - not sure + ALTITUDE_ACCURACY("50", 0,"ALTITUDE ACCURACY"), + CIRCLE_2D("51", 10, "CIRCLE 2D"), + TIME("52", 0,"TIME"), + ALTITUDE("54", 0,"ALTITUDE"), + CIRCLE_3D("55", 15, "CIRCLE 3D"), + HEADING("56", 1, "HEADING"), + HORIZONTAL_DIRECTION("57", 0,"HORIZONTAL DIRECTION"), + REQUEST_61("61", 1, "REQUEST-61"), + REQUEST_62("62", 0,"REQUEST-62"), + REQUEST_64("64", 0,"REQUEST-64"), + POINT_2D("66", 8, "POINT 2D"), + POINT_3D("69", 11, "POINT 3D"), + SPEED("6C", 2, "SPEED"), + REQUEST_73("73", 1, "REQUEST-73"), + TRIGGER_ON_MOVE("78", 1, "TRIGGER ON MOVE"), + REQUESTED_TOKENS("000", 0, "REQUESTED TOKENS"), + UNKNOWN("0",0, "UNKNOWN"); private String mValue; private int mLength; + private String mLabel; + + /** + * Tokens that have parameters in request packet messages + */ + private static EnumSet REQUEST_PARAMETER_TOKENS = EnumSet.of(IDENTITY, TRIGGER_DISTANCE, TRIGGER_GPIO, + TRIGGER_ON_MOVE, TRIGGER_PERIODIC, REQUEST_61, REQUEST_73); /** * Constructs an instance * @param value of the token * @param length of the token, exclusive of the identifier, where -1 indicates a variable length token. */ - TokenType(String value, int length) + TokenType(String value, int length, String label) { mValue = value; mLength = length; + mLabel = label; } /** @@ -83,6 +106,15 @@ public enum TokenType return mLength; } + /** + * Indicates if this is a request type token that has an optional parameter (e.g. the trigger tokens). + */ + public boolean isRequestParameterToken() + { + return REQUEST_PARAMETER_TOKENS.contains(this); + } + + private static final Map LOOKUP_MAP = new HashMap<>(); static @@ -107,4 +139,10 @@ public enum TokenType return UNKNOWN; } + + @Override + public String toString() + { + return mLabel; + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerDistance.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerDistance.java new file mode 100644 index 00000000..542eb565 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerDistance.java @@ -0,0 +1,65 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +import java.text.DecimalFormat; + +/** + * LRRP Trigger Distance Token + * + * Total Length: 2 bytes + */ +public class TriggerDistance extends Token +{ + private static final int[] DISTANCE = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; + + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public TriggerDistance(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.TRIGGER_DISTANCE; + } + + /** + * Distance for trigger + */ + public int getDistance() + { + return getMessage().getInt(DISTANCE, getOffset()); + } + + @Override + public String toString() + { + return "TRIGGER DISTANCE:" + getDistance(); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerGpio.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerGpio.java new file mode 100644 index 00000000..81a7e9cc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerGpio.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * LRRP Trigger on GPIO Token + * + * Total Length: 1 byte + */ +public class TriggerGpio extends Token +{ + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public TriggerGpio(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.TRIGGER_GPIO; + } + + @Override + public String toString() + { + return "TRIGGER ON GPIO"; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerOnMove.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerOnMove.java new file mode 100644 index 00000000..2e687442 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerOnMove.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * LRRP Trigger on Move Token + * + * Total Length: 1 byte + */ +public class TriggerOnMove extends Token +{ + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public TriggerOnMove(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.TRIGGER_ON_MOVE; + } + + @Override + public String toString() + { + return "TRIGGER ON MOVE"; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerPeriodic.java b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerPeriodic.java new file mode 100644 index 00000000..a663b603 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/ip/lrrp/token/TriggerPeriodic.java @@ -0,0 +1,63 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.ip.lrrp.token; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; + +/** + * LRRP Trigger Periodic Token + * + * Total Length: 2 bytes + */ +public class TriggerPeriodic extends Token +{ + private static final int[] SECONDS = new int[]{8, 9, 10, 11, 12, 13, 14, 15}; + + /** + * Constructs an instance of a heading token. + * + * @param message containing the heading + * @param offset to the start of the token + */ + public TriggerPeriodic(CorrectedBinaryMessage message, int offset) + { + super(message, offset); + } + + @Override + public TokenType getTokenType() + { + return TokenType.TRIGGER_DISTANCE; + } + + /** + * Seconds for trigger + */ + public int getSeconds() + { + return getMessage().getInt(SECONDS, getOffset()); + } + + @Override + public String toString() + { + return "TRIGGER EVERY:" + getSeconds() + " SECS"; + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java index f99b0781..4fef8d91 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java @@ -36,11 +36,16 @@ import io.github.dsheirer.message.StuffBitsMessage; 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.ip.IPacket; +import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet; +import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket; +import io.github.dsheirer.module.decode.ip.udp.UDPPacket; import io.github.dsheirer.module.decode.p25.audio.P25P1AudioModule; 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.preference.UserPreferences; @@ -58,6 +63,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.util.function.Consumer; /** @@ -446,7 +452,7 @@ public class P25P1MessageFramer implements Listener, IP25P1DataUnitDetect public static void main(String[] args) { - Path directory = Paths.get("/home/denny/SDRTrunk/recordings/LRRP P25"); + Path directory = Paths.get("/home/denny/Documents/TMR/APCO25/GPS/bkmzk007"); // Path directory = Paths.get("/media/denny/500G1EXT4/PBITRecordings"); UserPreferences userPreferences = new UserPreferences(); @@ -468,6 +474,7 @@ public class P25P1MessageFramer implements Listener, IP25P1DataUnitDetect mLog.info("Processing Directory: " + directory.toString()); + try(OutputStream logOutput = Files.newOutputStream(directory.resolve("log.txt"))) { try @@ -504,6 +511,22 @@ public class P25P1MessageFramer implements Listener, IP25P1DataUnitDetect { logOutput.write(message.toString().getBytes()); logOutput.write("\n".getBytes()); + + if(message instanceof PacketMessage packet) + { + if(packet.getPacket() instanceof IPV4Packet ipv4) + { + if(ipv4.getPayload() instanceof UDPPacket udp) + { + if(udp.getPayload() instanceof LRRPPacket lrrp) + { + logOutput.write("\n".getBytes()); + logOutput.write(lrrp.getMessage().toHexString().getBytes()); + logOutput.write("\n\n".getBytes()); + } + } + } + } } catch(IOException ioe) { diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java index f3ecd0d6..09ccf88d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java @@ -1,23 +1,20 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2021 Dennis Sheirer * - * * ****************************************************************************** - * * 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 - * * ***************************************************************************** + * 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 + * **************************************************************************** */ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk; @@ -171,7 +168,7 @@ public enum Opcode MOTOROLA_OSP_PATCH_GROUP_DELETE(1, "PATCH GROUP DELE", "MOTOROLA PATCH GROUP DELETE"), MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT(2, "PTCH GRP VCHN GR", "MOTOROLA PATCH GROUP CHANNEL GRANT"), MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE(3, "PTCH GRP VCH UPD", "MOTOROLA PATCH GROUP CHANNEL GRANT UPDATE"), - MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "TRAFFIC CHANNEL", "TRAFFIC CHANNEL"), + MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "TRAFFIC CHANNEL", "MOTOROLA TRAFFIC CHANNEL"), MOTOROLA_OSP_DENY_RESPONSE(7, "DENY RESPONSE", "MOTOROLA DENY RESPONSE"), MOTOROLA_OSP_SYSTEM_LOADING(9, "SYSTEM LOADING", "SYSTEM LOADING"), MOTOROLA_OSP_BASE_STATION_ID(11, "CCH BASE STAT ID", "CONTROL CHANNEL BASE STATION ID"), diff --git a/src/main/java/io/github/dsheirer/util/PacketUtil.java b/src/main/java/io/github/dsheirer/util/PacketUtil.java index 1f8dd6b4..95e1094b 100644 --- a/src/main/java/io/github/dsheirer/util/PacketUtil.java +++ b/src/main/java/io/github/dsheirer/util/PacketUtil.java @@ -22,8 +22,7 @@ package io.github.dsheirer.util; import io.github.dsheirer.module.decode.ip.IPacket; import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet; import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket; -import io.github.dsheirer.module.decode.ip.lrrp.token.GeoFencePosition; -import io.github.dsheirer.module.decode.ip.lrrp.token.Position; +import io.github.dsheirer.module.decode.ip.lrrp.token.Point2d; import io.github.dsheirer.module.decode.ip.lrrp.token.Token; import io.github.dsheirer.module.decode.ip.udp.UDPPacket; import org.jdesktop.swingx.mapviewer.GeoPosition; @@ -72,10 +71,10 @@ public class PacketUtil for(Token token : lrrpPacket.getTokens()) { - if(token instanceof Position) + if(token instanceof Point2d) { - Position position = (Position) token; - return new GeoPosition(position.getLatitude(), position.getLongitude()); + Point2d point2d = (Point2d) token; + return new GeoPosition(point2d.getLatitude(), point2d.getLongitude()); } } }