mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -64,6 +64,7 @@ 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.l3harris.LCHarrisReturnToControlChannel;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasComplete;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerGPSComplete;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaEmergencyAlarmActivation;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkComplete;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaUnitGPS;
|
||||
@@ -326,6 +327,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
{
|
||||
processTalkerAlias(talkerAlias);
|
||||
}
|
||||
else if(iMessage instanceof LinkControlWord lcw)
|
||||
{
|
||||
processLC(lcw, iMessage.getTimestamp(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2093,6 +2098,26 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
broadcast(event);
|
||||
}
|
||||
break;
|
||||
case L3HARRIS_TALKER_GPS_COMPLETE:
|
||||
if(lcw instanceof LCHarrisTalkerGPSComplete gps)
|
||||
{
|
||||
mTrafficChannelManager.processP1TrafficCurrentUser(getCurrentFrequency(), gps.getLocation(), timestamp, lcw.toString());
|
||||
|
||||
//We want to preserve the current TO/FROM and any other identifiers for the GPS event and add the GPS location
|
||||
MutableIdentifierCollection mic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
mic.update(gps.getIdentifiers());
|
||||
|
||||
PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, timestamp)
|
||||
.location(gps.getGeoPosition())
|
||||
.channel(getCurrentChannel())
|
||||
.details(gps.getLocation().toString())
|
||||
.end(timestamp)
|
||||
.protocol(Protocol.APCO25)
|
||||
.identifiers(mic)
|
||||
.build();
|
||||
broadcast(event);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// if(lcw.getVendor().isLoggable())
|
||||
// {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -29,6 +29,9 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.IExtendedSourceMes
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.HarrisTalkerAliasAssembler;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasBase;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerGPSBlock1;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerGPSBlock2;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerGPSComplete;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaTalkerAliasAssembler;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.standard.LCSourceIDExtension;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
|
||||
@@ -68,6 +71,8 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
private HDUMessage mHeldHDUMessage;
|
||||
private LDU1Message mHeldLDU1Message;
|
||||
private LDU2Message mHeldLDU2Message;
|
||||
private LCHarrisTalkerGPSBlock1 mHeldHarrisGPSBlock1;
|
||||
private long mHeldHarrisGPSBlock1Timestamp;
|
||||
|
||||
/**
|
||||
* Motorola talker alias assembler for link control header and data blocks.
|
||||
@@ -139,6 +144,26 @@ public class P25P1MessageProcessor implements Listener<IMessage>
|
||||
processSourceIDExtension(null);
|
||||
|
||||
dispatch(ldu1);
|
||||
|
||||
//Process Harris Talker GPS messages
|
||||
if(lcw instanceof LCHarrisTalkerGPSBlock1 block1)
|
||||
{
|
||||
mHeldHarrisGPSBlock1 = block1;
|
||||
mHeldHarrisGPSBlock1Timestamp = ldu1.getTimestamp();
|
||||
}
|
||||
else if(lcw instanceof LCHarrisTalkerGPSBlock2 block2 && mHeldHarrisGPSBlock1 != null)
|
||||
{
|
||||
//Ensure block 1 and block 2 are within 2 seconds of each other
|
||||
if(Math.abs(ldu1.getTimestamp() - mHeldHarrisGPSBlock1Timestamp) < 2000)
|
||||
{
|
||||
LCHarrisTalkerGPSComplete gps = LCHarrisTalkerGPSComplete.create(mHeldHarrisGPSBlock1, block2,
|
||||
ldu1.getTimestamp());
|
||||
dispatch(gps);
|
||||
}
|
||||
|
||||
mHeldHarrisGPSBlock1 = null;
|
||||
mHeldHarrisGPSBlock1Timestamp = 0;
|
||||
}
|
||||
}
|
||||
else if(message instanceof LDU2Message ldu2)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -106,8 +106,9 @@ public enum LinkControlOpcode
|
||||
MOTOROLA_UNKNOWN("MOTOROLA UNKNOWN", -1),
|
||||
|
||||
L3HARRIS_RETURN_TO_CONTROL_CHANNEL("UNKNOWN OPCODE 10", 10),
|
||||
L3HARRIS_UNKNOWN_2A("UNKNOWN OPCODE 42", 42),
|
||||
L3HARRIS_UNKNOWN_2B("UNKNOWN OPCODE 43", 43),
|
||||
L3HARRIS_TALKER_GPS_BLOCK1("TALKER GPS 1/2", 42),
|
||||
L3HARRIS_TALKER_GPS_BLOCK2("TALKER GPS 2/2", 43),
|
||||
L3HARRIS_TALKER_GPS_COMPLETE("TALKER GPS", -2),
|
||||
L3HARRIS_TALKER_ALIAS_BLOCK_1("TALKER ALIAS BLOCK 1", 50),
|
||||
L3HARRIS_TALKER_ALIAS_BLOCK_2("TALKER ALIAS BLOCK 2", 51),
|
||||
L3HARRIS_TALKER_ALIAS_BLOCK_3("TALKER ALIAS BLOCK 3", 52),
|
||||
@@ -151,8 +152,7 @@ public enum LinkControlOpcode
|
||||
/**
|
||||
* L3Harris Opcodes
|
||||
*/
|
||||
public static final EnumSet<LinkControlOpcode> L3HARRIS_OPCODES = EnumSet.of(L3HARRIS_RETURN_TO_CONTROL_CHANNEL,
|
||||
L3HARRIS_UNKNOWN_2A, L3HARRIS_UNKNOWN_2B, L3HARRIS_TALKER_ALIAS_BLOCK_1, L3HARRIS_TALKER_ALIAS_BLOCK_2,
|
||||
public static final EnumSet<LinkControlOpcode> L3HARRIS_OPCODES = EnumSet.of(L3HARRIS_RETURN_TO_CONTROL_CHANNEL, L3HARRIS_TALKER_GPS_BLOCK1, L3HARRIS_TALKER_GPS_BLOCK2, L3HARRIS_TALKER_ALIAS_BLOCK_1, L3HARRIS_TALKER_ALIAS_BLOCK_2,
|
||||
L3HARRIS_TALKER_ALIAS_BLOCK_3, L3HARRIS_TALKER_ALIAS_BLOCK_4, L3HARRIS_UNKNOWN);
|
||||
|
||||
/**
|
||||
@@ -221,9 +221,9 @@ public enum LinkControlOpcode
|
||||
case 10:
|
||||
return L3HARRIS_RETURN_TO_CONTROL_CHANNEL;
|
||||
case 42:
|
||||
return L3HARRIS_UNKNOWN_2A;
|
||||
return L3HARRIS_TALKER_GPS_BLOCK1;
|
||||
case 43:
|
||||
return L3HARRIS_UNKNOWN_2B;
|
||||
return L3HARRIS_TALKER_GPS_BLOCK2;
|
||||
case 50:
|
||||
return L3HARRIS_TALKER_ALIAS_BLOCK_1;
|
||||
case 51:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -25,8 +25,8 @@ import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisT
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasBlock2;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasBlock3;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerAliasBlock4;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisUnknownOpcode42;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisUnknownOpcode43;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerGPSBlock1;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris.LCHarrisTalkerGPSBlock2;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaEmergencyAlarmActivation;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaFailsoft;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.motorola.LCMotorolaGroupGroupDelete;
|
||||
@@ -161,10 +161,10 @@ public class LinkControlWordFactory
|
||||
|
||||
case L3HARRIS_RETURN_TO_CONTROL_CHANNEL:
|
||||
return new LCHarrisReturnToControlChannel(message);
|
||||
case L3HARRIS_UNKNOWN_2A:
|
||||
return new LCHarrisUnknownOpcode42(message);
|
||||
case L3HARRIS_UNKNOWN_2B:
|
||||
return new LCHarrisUnknownOpcode43(message);
|
||||
case L3HARRIS_TALKER_GPS_BLOCK1:
|
||||
return new LCHarrisTalkerGPSBlock1(message);
|
||||
case L3HARRIS_TALKER_GPS_BLOCK2:
|
||||
return new LCHarrisTalkerGPSBlock2(message);
|
||||
case L3HARRIS_TALKER_ALIAS_BLOCK_1:
|
||||
return new LCHarrisTalkerAliasBlock1(message);
|
||||
case L3HARRIS_TALKER_ALIAS_BLOCK_2:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -26,19 +26,16 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* L3Harris Unknown Opcode 43 (0x2B)
|
||||
* <p>
|
||||
* Observed in a voice traffic channel carried by an LDU1. This was transmitted once following an Opcode 42 (0x2A)
|
||||
* which is also unknown.
|
||||
* L3Harris Talker GPS - Block 1/2 (Opcode:0x2A)
|
||||
*/
|
||||
public class LCHarrisUnknownOpcode43 extends LinkControlWord
|
||||
public class LCHarrisTalkerGPSBlock1 extends LinkControlWord
|
||||
{
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public LCHarrisUnknownOpcode43(CorrectedBinaryMessage message)
|
||||
public LCHarrisTalkerGPSBlock1(CorrectedBinaryMessage message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
@@ -58,7 +55,7 @@ public class LCHarrisUnknownOpcode43 extends LinkControlWord
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("L3HARRIS UNKNOWN OPCODE 43 MSG:").append(getMessage().toHexString());
|
||||
sb.append("L3HARRIS TALKER GPS BLOCK 1/2 MSG:").append(getMessage().toHexString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -26,19 +26,16 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* L3Harris Unknown Opcode 42 (0x2A)
|
||||
* <p>
|
||||
* Observed in a voice traffic channel carried by an LDU1. This was transmitted once followed by an Opcode 43 (0x2B)
|
||||
* which is also unknown.
|
||||
* L3Harris Talker GPS - Block 2/2 (Opcode:0x2B)
|
||||
*/
|
||||
public class LCHarrisUnknownOpcode42 extends LinkControlWord
|
||||
public class LCHarrisTalkerGPSBlock2 extends LinkControlWord
|
||||
{
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public LCHarrisUnknownOpcode42(CorrectedBinaryMessage message)
|
||||
public LCHarrisTalkerGPSBlock2(CorrectedBinaryMessage message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
@@ -58,7 +55,7 @@ public class LCHarrisUnknownOpcode42 extends LinkControlWord
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("L3HARRIS UNKNOWN OPCODE 42 MSG:").append(getMessage().toHexString());
|
||||
sb.append("L3HARRIS TALKER GPS BLOCK 2/2 MSG:").append(getMessage().toHexString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.lc.l3harris;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.P25Location;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25P1Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlWord;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris.L3HarrisGPS;
|
||||
import io.github.dsheirer.module.decode.p25.reference.Vendor;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
|
||||
/**
|
||||
* L3Harris Talker GPS Complete. This message is assembled from FLC L3Harris Talker GPS Block 1 and Block 2.
|
||||
*/
|
||||
public class LCHarrisTalkerGPSComplete extends LinkControlWord implements IMessage
|
||||
{
|
||||
private static final DecimalFormat GPS_FORMAT = new DecimalFormat("0.000000");
|
||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss");
|
||||
static {
|
||||
SDF.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
private P25Location mLocation;
|
||||
private GeoPosition mGeoPosition;
|
||||
private List<Identifier> mIdentifiers;
|
||||
private long mTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs a Link Control Word from the binary message sequence.
|
||||
* @param message constructed from block 1 and block 2 fragments
|
||||
*/
|
||||
private LCHarrisTalkerGPSComplete(CorrectedBinaryMessage message, long timestamp)
|
||||
{
|
||||
super(message);
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return Protocol.APCO25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeslot()
|
||||
{
|
||||
return P25P1Message.TIMESLOT_0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of this message from block 1 and block 2 FLC GPS message fragments.
|
||||
* @param block1 FLC Harris Talker GPS
|
||||
* @param block2 FLC Harris Talker GPS
|
||||
* @param timestamp for the message
|
||||
* @return instance of this message.
|
||||
*/
|
||||
public static LCHarrisTalkerGPSComplete create(LCHarrisTalkerGPSBlock1 block1, LCHarrisTalkerGPSBlock2 block2,
|
||||
long timestamp)
|
||||
{
|
||||
CorrectedBinaryMessage cbm = new CorrectedBinaryMessage(112);
|
||||
cbm.load(0, block1.getMessage().getSubMessage(16, 72));
|
||||
cbm.load(56, block2.getMessage().getSubMessage(16, 72));
|
||||
return new LCHarrisTalkerGPSComplete(cbm, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Textual representation of this message
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("L3HARRIS TALKER GPS ");
|
||||
GeoPosition geo = getGeoPosition();
|
||||
sb.append(GPS_FORMAT.format(geo.getLatitude())).append(" ").append(GPS_FORMAT.format(geo.getLongitude()));
|
||||
sb.append(" HEADING:").append(getHeading());
|
||||
sb.append(" TIME:").append(SDF.format(getTimestampMs()));
|
||||
sb.append(" UTC MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vendor getVendor()
|
||||
{
|
||||
return Vendor.HARRIS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkControlOpcode getOpcode()
|
||||
{
|
||||
return LinkControlOpcode.L3HARRIS_TALKER_GPS_COMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpcodeNumber()
|
||||
{
|
||||
return getOpcode().getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Heading
|
||||
* @return heading, 0-359 degrees.
|
||||
*/
|
||||
public int getHeading()
|
||||
{
|
||||
return L3HarrisGPS.parseHeading(getMessage(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* GPS Position time in milliseconds.
|
||||
* @return time in ms UTC
|
||||
*/
|
||||
public long getTimestampMs()
|
||||
{
|
||||
return L3HarrisGPS.parseTimestamp(getMessage(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* GPS Location
|
||||
* @return location in decimal degrees
|
||||
*/
|
||||
public P25Location getLocation()
|
||||
{
|
||||
if(mLocation == null)
|
||||
{
|
||||
GeoPosition geoPosition = getGeoPosition();
|
||||
mLocation = P25Location.createFrom(geoPosition.getLatitude(), geoPosition.getLongitude());
|
||||
}
|
||||
|
||||
return mLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Geo position
|
||||
* @return position
|
||||
*/
|
||||
public GeoPosition getGeoPosition()
|
||||
{
|
||||
if(mGeoPosition == null)
|
||||
{
|
||||
mGeoPosition = new GeoPosition(L3HarrisGPS.parseLatitude(getMessage(), 0),
|
||||
L3HarrisGPS.parseLongitude(getMessage(), 0));
|
||||
}
|
||||
|
||||
return mGeoPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getLocation());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Utility class to parse L3Harris GPS data from P25 Phase 1 and Phase 2 messages.
|
||||
*/
|
||||
public class L3HarrisGPS
|
||||
{
|
||||
public static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss");
|
||||
static {
|
||||
SDF.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
private static final DecimalFormat GPS_FORMAT = new DecimalFormat("0.000000");
|
||||
|
||||
//Bits 0 & 1 not set in sample data - seems unused for a 1/10000th of a minute number system
|
||||
private static final IntField LAT_MINUTES_FRACTIONAL = IntField.range(0, 15);
|
||||
private static final int LAT_HEMISPHERE = 16;
|
||||
//Bit 39 & 41 not set in sample data
|
||||
private static final IntField LAT_MINUTES = IntField.range(17, 23);
|
||||
private static final IntField LAT_DEGREES = IntField.range(24, 31);
|
||||
|
||||
//Bits 56 & 57 not set in sample data - seems unused for a 1/5000th of a minute number system
|
||||
private static final IntField LONG_MINUTES_FRACTIONAL = IntField.range(32, 47);
|
||||
private static final int LONG_HEMISPHERE = 48;
|
||||
//Bit 71 & 73 not set in sample data
|
||||
private static final IntField LONG_MINUTES = IntField.range(49, 55);
|
||||
private static final IntField LONG_DEGREES = IntField.range(56, 63);
|
||||
|
||||
//There's a leading bit missing from GPS Time to get from (2^16) to (2^17) needed address space (86,400 total seconds)
|
||||
private static final IntField GPS_TIME = IntField.range(64, 79);
|
||||
private static final int GPS_TIME_MSB = 80;
|
||||
|
||||
//This may not be accurate.
|
||||
private static final IntField HEADING = IntField.range(95, 103);
|
||||
|
||||
/**
|
||||
* Parses the latitude value from the message
|
||||
* @param message containing latitude value.
|
||||
* @param offset into the message to the start of the formatted GPS data.
|
||||
* @return latitude in degrees decimal.
|
||||
*/
|
||||
public static double parseLatitude(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
return parseCoordinate(message, offset, LAT_DEGREES, LAT_MINUTES, LAT_MINUTES_FRACTIONAL, LAT_HEMISPHERE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the longitude value from the message
|
||||
* @param message containing longitude value.
|
||||
* @param offset into the message to the start of the formatted GPS data.
|
||||
* @return longitude in degrees decimal.
|
||||
*/
|
||||
public static double parseLongitude(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
return parseCoordinate(message, offset, LONG_DEGREES, LONG_MINUTES, LONG_MINUTES_FRACTIONAL, LONG_HEMISPHERE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the latitude or longitude coordinate value from the message
|
||||
* @param message containing the coordinate
|
||||
* @param offset into the message to the start of the GPS data
|
||||
* @param degrees field definition
|
||||
* @param minutes field definition
|
||||
* @param fractional field definition
|
||||
* @param hemisphere field definition
|
||||
* @return value in degrees decimal.
|
||||
*/
|
||||
private static double parseCoordinate(CorrectedBinaryMessage message, int offset, IntField degrees,
|
||||
IntField minutes, IntField fractional, int hemisphere)
|
||||
{
|
||||
double longitude = message.getInt(degrees, offset);
|
||||
longitude += ((message.getInt(minutes, offset) + (message.getInt(fractional, offset) / 10000.0)) / 60.0);
|
||||
longitude *= (message.get(hemisphere + offset) ? -1 : 1);
|
||||
return longitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the timestamp
|
||||
* @param message containing the timestamp
|
||||
* @param offset into the message to the start of the GPS data.
|
||||
* @return timestamp in milliseconds
|
||||
*/
|
||||
public static long parseTimestamp(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
long seconds = message.getInt(GPS_TIME, offset);
|
||||
|
||||
if(message.get(offset + GPS_TIME_MSB))
|
||||
{
|
||||
seconds *= 2;
|
||||
}
|
||||
|
||||
return seconds * 1000; //Convert seconds to milliseconds.
|
||||
}
|
||||
|
||||
/**
|
||||
* Heading
|
||||
*
|
||||
* @return heading, 0-359 degrees.
|
||||
*/
|
||||
public static int parseHeading(CorrectedBinaryMessage message, int offset)
|
||||
{
|
||||
return message.getInt(HEADING, offset);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -20,7 +20,6 @@
|
||||
package io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.l3harris;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.P25Location;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.MacStructureVendor;
|
||||
@@ -39,37 +38,12 @@ import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
*/
|
||||
public class L3HarrisTalkerGpsLocation extends MacStructureVendor
|
||||
{
|
||||
public static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss");
|
||||
private static final DecimalFormat GPS_FORMAT = new DecimalFormat("0.000000");
|
||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss");
|
||||
static {
|
||||
SDF.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
private static final DecimalFormat GPS_FORMAT = new DecimalFormat("0.000000");
|
||||
private static final DecimalFormat FIXED = new DecimalFormat("000");
|
||||
|
||||
//Bits 24 & 25 not set in sample data - seems unused for a 1/5000th of a minute number system
|
||||
private static final IntField LATITUDE_MINUTES_FRACTIONAL = IntField.range(24, 38);
|
||||
private static final int LATITUDE_HEMISPHERE = 40;
|
||||
//Bit 39 & 41 not set in sample data
|
||||
private static final IntField LATITUDE_MINUTES = IntField.range(42, 47);
|
||||
private static final IntField LATITUDE_DEGREES = IntField.range(48, 55);
|
||||
|
||||
//Bits 56 & 57 not set in sample data - seems unused for a 1/5000th of a minute number system
|
||||
private static final IntField LONGITUDE_MINUTES_FRACTIONAL = IntField.range(56, 70);
|
||||
private static final int LONGITUDE_HEMISPHERE = 72;
|
||||
//Bit 71 & 73 not set in sample data
|
||||
private static final IntField LONGITUDE_MINUTES = IntField.range(74, 79);
|
||||
private static final IntField LONGITUDE_DEGREES = IntField.range(80, 87);
|
||||
|
||||
//There's a leading bit missing from GPS Time to get from (2^16) to (2^17) needed address space (86,400 total seconds)
|
||||
private static final IntField GPS_TIME = IntField.range(88, 103);
|
||||
|
||||
private static final IntField U1 = IntField.length8(OCTET_14_BIT_104);
|
||||
private static final IntField U2 = IntField.length8(OCTET_15_BIT_112);
|
||||
private static final IntField U3 = IntField.length8(OCTET_16_BIT_120);
|
||||
private static final IntField U4 = IntField.length8(OCTET_17_BIT_128);
|
||||
|
||||
//This may not be accurate.
|
||||
private static final IntField HEADING = IntField.range(119, 127);
|
||||
private static final int DATA_OFFSET = 24;
|
||||
|
||||
private P25Location mLocation;
|
||||
private GeoPosition mGeoPosition;
|
||||
@@ -102,10 +76,11 @@ public class L3HarrisTalkerGpsLocation extends MacStructureVendor
|
||||
sb.append("VENDOR:").append(getVendor()).append(" TALKER GPS ");
|
||||
}
|
||||
|
||||
sb.append(GPS_FORMAT.format(getLatitude())).append(" ").append(GPS_FORMAT.format(getLongitude()));
|
||||
GeoPosition geo = getGeoPosition();
|
||||
sb.append(GPS_FORMAT.format(geo.getLatitude())).append(" ").append(GPS_FORMAT.format(geo.getLongitude()));
|
||||
sb.append(" HEADING:").append(getHeading());
|
||||
sb.append(" TIME:").append(SDF.format(getTimestampMs()));
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
sb.append(" UTC MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -115,7 +90,7 @@ public class L3HarrisTalkerGpsLocation extends MacStructureVendor
|
||||
*/
|
||||
public int getHeading()
|
||||
{
|
||||
return getInt(HEADING);
|
||||
return L3HarrisGPS.parseHeading(getMessage(), getOffset() + DATA_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +99,7 @@ public class L3HarrisTalkerGpsLocation extends MacStructureVendor
|
||||
*/
|
||||
public long getTimestampMs()
|
||||
{
|
||||
return getInt(GPS_TIME) * 1000; //Convert seconds to milliseconds.
|
||||
return L3HarrisGPS.parseTimestamp(getMessage(), getOffset() + DATA_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +110,8 @@ public class L3HarrisTalkerGpsLocation extends MacStructureVendor
|
||||
{
|
||||
if(mLocation == null)
|
||||
{
|
||||
mLocation = P25Location.createFrom(getLatitude(), getLongitude());
|
||||
GeoPosition geoPosition = getGeoPosition();
|
||||
mLocation = P25Location.createFrom(geoPosition.getLatitude(), geoPosition.getLongitude());
|
||||
}
|
||||
|
||||
return mLocation;
|
||||
@@ -149,62 +125,13 @@ public class L3HarrisTalkerGpsLocation extends MacStructureVendor
|
||||
{
|
||||
if(mGeoPosition == null)
|
||||
{
|
||||
mGeoPosition = new GeoPosition(getLatitude(), getLongitude());
|
||||
mGeoPosition = new GeoPosition(L3HarrisGPS.parseLatitude(getMessage(), getOffset() + DATA_OFFSET),
|
||||
L3HarrisGPS.parseLongitude(getMessage(), getOffset() + DATA_OFFSET));
|
||||
}
|
||||
|
||||
return mGeoPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* GPS location - latitude
|
||||
* @return latitude degrees decimal
|
||||
*/
|
||||
public double getLatitude()
|
||||
{
|
||||
return (getLatitudeDegrees() + (getLatitudeMinutes() / 60.0)) * (getMessage().get(LATITUDE_HEMISPHERE + getOffset()) ? -1 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Latitude degrees
|
||||
*/
|
||||
private double getLatitudeDegrees()
|
||||
{
|
||||
return getInt(LATITUDE_DEGREES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Latitude minutes
|
||||
*/
|
||||
private double getLatitudeMinutes()
|
||||
{
|
||||
return getInt(LATITUDE_MINUTES) + getInt(LATITUDE_MINUTES_FRACTIONAL) / 5000d;
|
||||
}
|
||||
|
||||
/**
|
||||
* GPS location - longitude
|
||||
* @return longitude degrees decimal
|
||||
*/
|
||||
public double getLongitude()
|
||||
{
|
||||
return (getLongitudeDegrees() + (getLongitudeMinutes() / 60.0)) * (getMessage().get(LONGITUDE_HEMISPHERE + getOffset()) ? -1 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Longitude degrees
|
||||
*/
|
||||
private double getLongitudeDegrees()
|
||||
{
|
||||
return getInt(LONGITUDE_DEGREES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Longitude minutes
|
||||
*/
|
||||
private double getLongitudeMinutes()
|
||||
{
|
||||
return getInt(LONGITUDE_MINUTES) + getInt(LONGITUDE_MINUTES_FRACTIONAL) / 5000d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user