diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java index 1b4019bcb..fcbeb1642 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java @@ -57,6 +57,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage; import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HeaderData; import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord; +import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaUnitGPS; import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCCallTermination; import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCExtendedFunctionCommand; import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCMessageUpdate; @@ -1885,6 +1886,22 @@ private void processLinkControl(LinkControlWord lcw, long timestamp) case UNIT_TO_UNIT_ANSWER_REQUEST: processBroadcast(lcw.getIdentifiers(), timestamp, DecodeEventType.PAGE, "Unit-to-Unit Answer Request"); break; + case MOTOROLA_UNIT_GPS: + if(lcw instanceof LCMotorolaUnitGPS moto) + { + String gpsDetails = "LOCATION: " + moto.getLatitude() + " " + moto.getLongitude(); + + PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, timestamp) + .location(new GeoPosition(moto.getLatitude(), moto.getLongitude())) + .channel(getCurrentChannel()) + .details(gpsDetails) + .end(timestamp) + .protocol(Protocol.APCO25) + .identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers())) + .build(); + broadcast(event); + } + break; default: // mLog.debug("Unrecognized LCW Opcode: " + lcw.getOpcode().name() + " VENDOR:" + lcw.getVendor() + // " OPCODE:" + lcw.getOpcodeNumber()); diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java index 0d039e781..b3b06a746 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java @@ -96,6 +96,7 @@ public enum LinkControlOpcode MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_UPDATE("PATCH GROUP VOICE CHANNEL UPDATE", 1), MOTOROLA_PATCH_GROUP_ADD("PATCH GROUP ADD", 3), MOTOROLA_PATCH_GROUP_DELETE("PATCH GROUP DELETE", 4), + MOTOROLA_UNIT_GPS("UNIT GPS", 6), MOTOROLA_TALK_COMPLETE("TALK_COMPLETE", 15), MOTOROLA_UNKNOWN("UNKNOWN", -1), @@ -201,6 +202,8 @@ public static LinkControlOpcode fromValue(int value, Vendor vendor) return MOTOROLA_PATCH_GROUP_ADD; case 4: return MOTOROLA_PATCH_GROUP_DELETE; + case 6: + return MOTOROLA_UNIT_GPS; case 15: return MOTOROLA_TALK_COMPLETE; default: diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWordFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWordFactory.java index eb5719284..4b0be6fd3 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWordFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlWordFactory.java @@ -1,7 +1,6 @@ /* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +14,7 @@ * * 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.lc; @@ -26,6 +25,7 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaPatchGroupVoiceChannelUpdate; import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaPatchGroupVoiceChannelUser; import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkComplete; +import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaUnitGPS; import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaUnknownOpcode; import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCAdjacentSiteStatusBroadcast; import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCAdjacentSiteStatusBroadcastExplicit; @@ -138,6 +138,8 @@ public static LinkControlWord create(BinaryMessage binaryMessage) return new LCMotorolaTalkComplete(binaryMessage); case MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_UPDATE: return new LCMotorolaPatchGroupVoiceChannelUpdate(binaryMessage); + case MOTOROLA_UNIT_GPS: + return new LCMotorolaUnitGPS(binaryMessage); case MOTOROLA_UNKNOWN: return new LCMotorolaUnknownOpcode(binaryMessage); diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaUnitGPS.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaUnitGPS.java new file mode 100644 index 000000000..ae48f5923 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/motorola/LCMotorolaUnitGPS.java @@ -0,0 +1,84 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola; + +import io.github.dsheirer.bits.BinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import java.util.Collections; +import java.util.List; + +/** + * Motorola Unit Self-Reported GPS Location + */ +public class LCMotorolaUnitGPS extends MotorolaLinkControlWord +{ + private static final double LATITUDE_MULTIPLIER = 90.0 / 0x7FFFFF; + private static final double LONGITUDE_MULTIPLIER = 180.0 / 0x7FFFFF; + + private static final int[] UNKNOWN = {16, 17, 18, 19, 20, 21, 22, 23}; + private static final int LATITUDE_SIGN = 24; + private static final int[] LATITUDE = {25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + private static final int LONGITUDE_SIGN = 48; + private static final int[] LONGITUDE = {49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71}; + + /** + * Constructs an instance + * @param message binary + */ + public LCMotorolaUnitGPS(BinaryMessage message) + { + super(message); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("MOTOROLA UNIT GPS LOCATION: ").append(getLatitude()).append(" ").append(getLongitude()); + return sb.toString(); + } + + /** + * GPS Latitude value. + * @return value in decimal degrees + */ + public double getLatitude() + { + return getMessage().getInt(LATITUDE) * LATITUDE_MULTIPLIER * (getMessage().get(LATITUDE_SIGN) ? -1 : 1); + } + + /** + * GPS Longitude value. + * @return value in decimal degrees + */ + public double getLongitude() + { + return getMessage().getInt(LONGITUDE) * LONGITUDE_MULTIPLIER * (getMessage().get(LONGITUDE_SIGN) ? -1 : 1); + } + + /** + * List of identifiers contained in this message + */ + @Override + public List getIdentifiers() + { + return Collections.emptyList(); + } +}