#1304 P25 Phase 1/2 decoder enhancements. (#1898)

* P25 Phase 2 now supports control channel decoding and decoder was enhanced to improve decodes.
* Radio reference site editor updated for phase 2 sites to allow user to select FDMA or TDMA control.
* Decode events are now minimized and de-duplicated.
* Fully parsing all phase 1/2 messages from the latest ICD.
* Vendor specific messaging recoveries (Moto & Harris).
* Patch group management enhancements with stale patch detection.
* Tuner editor now fully resets the min/max frequency values
* Default traffic channel count increased to 20 (from 3).
* ISSI roaming radio and talkgroup values now displayed and aliasable as fully qualified values (e.g. 1234(123.456.7890)
* Moto emergency alarm activation messaging support
* Expanded list of P25 encryption algorithms
* Message (bits) viewer enhanced for P25 phase 1 and phase 2
* IPV4 addresses and UDP ports no longer classified as user values - won't display in event views.
* New framework for describing message binary fields that should reduce overall memory footprint and enhance code readability

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2024-04-26 13:55:17 -04:00
committed by GitHub
parent f5938fa8e2
commit 7e709d9d6f
430 changed files with 30098 additions and 13300 deletions

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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,31 +21,41 @@ package io.github.dsheirer.preference.identifier.talkgroup;
import io.github.dsheirer.identifier.patch.PatchGroup;
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
import io.github.dsheirer.identifier.radio.FullyQualifiedRadioIdentifier;
import io.github.dsheirer.identifier.radio.RadioIdentifier;
import io.github.dsheirer.identifier.talkgroup.FullyQualifiedTalkgroupIdentifier;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.preference.identifier.IntegerFormat;
public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
{
public static final int GROUP_DECIMAL_WIDTH = 5;
public static final int UNIT_DECIMAL_WIDTH = 8;
public static final int GROUP_HEXADECIMAL_WIDTH = 4;
public static final int UNIT_HEXADECIMAL_WIDTH = 6;
public static final int RADIO_DECIMAL_WIDTH = 8;
public static final int RADIO_HEXADECIMAL_WIDTH = 6;
public static final int SYSTEM_HEXADECIMAL_WIDTH = 3;
public static final int TALKGROUP_DECIMAL_WIDTH = 5;
public static final int TALKGROUP_HEXADECIMAL_WIDTH = 4;
public static final int WACN_HEXADECIMAL_WIDTH = 5;
/**
* Formats the individual or group identifier to the specified format and width.
*/
public static String format(TalkgroupIdentifier identifier, IntegerFormat format, boolean fixedWidth)
{
if(identifier instanceof FullyQualifiedTalkgroupIdentifier fqti)
{
return format(fqti, format, fixedWidth);
}
if(fixedWidth)
{
switch(format)
{
case DECIMAL:
case FORMATTED:
return toDecimal(identifier.getValue(), GROUP_DECIMAL_WIDTH);
return toDecimal(identifier.getValue(), TALKGROUP_DECIMAL_WIDTH);
case HEXADECIMAL:
return toHex(identifier.getValue(), GROUP_HEXADECIMAL_WIDTH);
return toHex(identifier.getValue(), TALKGROUP_HEXADECIMAL_WIDTH);
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
}
@@ -65,6 +75,62 @@ public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
}
}
/**
* Formats the fully qualified group identifier to the specified format and width.
*/
public static String format(FullyQualifiedTalkgroupIdentifier identifier, IntegerFormat format, boolean fixedWidth)
{
int id = identifier.getValue();
int wacn = identifier.getWacn();
int system = identifier.getSystem();
int talkgroup = identifier.getTalkgroup();
StringBuilder sb = new StringBuilder();
if(fixedWidth)
{
switch(format)
{
case DECIMAL:
case FORMATTED:
sb.append(toDecimal(id, TALKGROUP_DECIMAL_WIDTH));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toDecimal(talkgroup, TALKGROUP_DECIMAL_WIDTH)).append(")");
return sb.toString();
case HEXADECIMAL:
sb.append(toHex(id, TALKGROUP_HEXADECIMAL_WIDTH));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(talkgroup, TALKGROUP_HEXADECIMAL_WIDTH)).append(")");
return sb.toString();
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
}
}
else
{
switch(format)
{
case DECIMAL:
case FORMATTED:
sb.append(id);
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(talkgroup).append(")");
return sb.toString();
case HEXADECIMAL:
sb.append(toHex(id));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(talkgroup)).append(")");
return sb.toString();
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
}
}
}
/**
* Formats the patch group to the specified format and width
*/
@@ -110,15 +176,20 @@ public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
*/
public static String format(RadioIdentifier identifier, IntegerFormat format, boolean fixedWidth)
{
if(identifier instanceof FullyQualifiedRadioIdentifier fqri)
{
return format(fqri, format, fixedWidth);
}
if(fixedWidth)
{
switch(format)
{
case DECIMAL:
case FORMATTED:
return toDecimal(identifier.getValue(), UNIT_DECIMAL_WIDTH);
return toDecimal(identifier.getValue(), RADIO_DECIMAL_WIDTH);
case HEXADECIMAL:
return toHex(identifier.getValue(), UNIT_HEXADECIMAL_WIDTH);
return toHex(identifier.getValue(), RADIO_HEXADECIMAL_WIDTH);
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
}
@@ -138,6 +209,62 @@ public class APCO25TalkgroupFormatter extends AbstractIntegerFormatter
}
}
/**
* Formats the fully qualified radio identifier to the specified format and width.
*/
public static String format(FullyQualifiedRadioIdentifier identifier, IntegerFormat format, boolean fixedWidth)
{
int id = identifier.getValue();
int wacn = identifier.getWacn();
int system = identifier.getSystem();
int radio = identifier.getRadio();
StringBuilder sb = new StringBuilder();
if(fixedWidth)
{
switch(format)
{
case DECIMAL:
case FORMATTED:
sb.append(toDecimal(id, RADIO_DECIMAL_WIDTH));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toDecimal(radio, RADIO_DECIMAL_WIDTH)).append(")");
return sb.toString();
case HEXADECIMAL:
sb.append(toHex(id, RADIO_HEXADECIMAL_WIDTH));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(radio, RADIO_HEXADECIMAL_WIDTH)).append(")");
return sb.toString();
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
}
}
else
{
switch(format)
{
case DECIMAL:
case FORMATTED:
sb.append(id);
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(radio).append(")");
return sb.toString();
case HEXADECIMAL:
sb.append(toHex(id));
sb.append("(").append(toHex(wacn, WACN_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(system, SYSTEM_HEXADECIMAL_WIDTH));
sb.append(".").append(toHex(radio)).append(")");
return sb.toString();
default:
throw new IllegalArgumentException("Unrecognized integer format: " + format);
}
}
}
@Override
public String format(int value, IntegerFormat integerFormat)
{