#1368 Restructures Message and Event filter framework.

This commit is contained in:
Dennis Sheirer
2023-07-12 04:41:57 -04:00
parent d246ffff30
commit b0a524cf62
121 changed files with 4869 additions and 4950 deletions

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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
@@ -21,7 +21,6 @@ package io.github.dsheirer.module;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.List;
@@ -50,7 +49,7 @@ public abstract class HistoryModule<T> extends Module implements Listener<T>
*/
public List<T> getItems()
{
return new ArrayList<>(mItems);
return new ArrayList<>(mItems);
}
@Override

View File

@@ -117,8 +117,8 @@ public class ProcessingChain implements Listener<ChannelEvent>
private Broadcaster<IMessage> mMessageBroadcaster = new Broadcaster<>();
private Broadcaster<SquelchStateEvent> mSquelchStateEventBroadcaster = new Broadcaster<>();
private AtomicBoolean mRunning = new AtomicBoolean();
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(500);
private MessageHistory mMessageHistory = new MessageHistory(500);
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(200);
private MessageHistory mMessageHistory = new MessageHistory(200);
private AbstractChannelState mChannelState;
private EventBus mEventBus;
protected Source mSource;

View File

@@ -47,6 +47,7 @@ import io.github.dsheirer.module.decode.dmr.DMRDecoderState;
import io.github.dsheirer.module.decode.dmr.DMRTrafficChannelManager;
import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
import io.github.dsheirer.module.decode.dmr.audio.DMRAudioModule;
import io.github.dsheirer.module.decode.dmr.message.filter.DmrMessageFilterSet;
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2Decoder;
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2DecoderState;
import io.github.dsheirer.module.decode.fleetsync2.FleetsyncMessageFilter;
@@ -80,16 +81,18 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderC4FM;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderLSM;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderState;
import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25MessageFilterSet;
import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25P1MessageFilterSet;
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderHDQPSK;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState;
import io.github.dsheirer.module.decode.p25.phase2.message.filter.P25P2MessageFilterSet;
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
import io.github.dsheirer.module.decode.passport.PassportDecoder;
import io.github.dsheirer.module.decode.passport.PassportDecoderState;
import io.github.dsheirer.module.decode.passport.PassportMessageFilter;
import io.github.dsheirer.module.decode.tait.Tait1200Decoder;
import io.github.dsheirer.module.decode.tait.Tait1200DecoderState;
import io.github.dsheirer.module.decode.tait.Tait1200MessageFilter;
import io.github.dsheirer.module.decode.traffic.TrafficChannelManager;
import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule;
import io.github.dsheirer.preference.UserPreferences;
@@ -494,7 +497,7 @@ public class DecoderFactory
*/
public static FilterSet<IMessage> getMessageFilters(List<Module> modules)
{
FilterSet<IMessage> filterSet = new FilterSet<>();
FilterSet<IMessage> filterSet = new FilterSet<>("Message Filters");
for(Module module : modules)
{
@@ -504,28 +507,28 @@ public class DecoderFactory
}
}
/* If we don't have any filters, add an ALL-PASS filter */
if(filterSet.getFilters().isEmpty())
{
filterSet.addFilter(new AllPassFilter<>());
}
//Add an all-others filter as a catch-all for anything that isn't handled by the decoder filters.
filterSet.addFilter(new AllPassFilter<>("All Other Messages Filter"));
return filterSet;
}
/**
* Returns a set of IMessageFilter objects (FilterSets or Filters) that
* can process all of the messages produced by the specified decoder type.
* can process all messages produced by the specified decoder type.
*/
public static List<IFilter<IMessage>> getMessageFilter(DecoderType decoder)
{
ArrayList<IFilter<IMessage>> filters = new ArrayList<>();
List<IFilter<IMessage>> filters = new ArrayList<>();
switch(decoder)
{
case DCS:
filters.add(new DCSMessageFilter());
break;
case DMR:
filters.add(new DmrMessageFilterSet());
break;
case FLEETSYNC2:
filters.add(new FleetsyncMessageFilter());
break;
@@ -545,13 +548,16 @@ public class DecoderFactory
filters.add(new MPT1327MessageFilter());
break;
case P25_PHASE1:
filters.add(new P25MessageFilterSet());
filters.add(new P25P1MessageFilterSet());
break;
case P25_PHASE2:
filters.add(new P25P2MessageFilterSet());
break;
case PASSPORT:
filters.add(new PassportMessageFilter());
break;
case DMR:
//filters.add(new DMR) //todo: not finished
case TAIT_1200:
filters.add(new Tait1200MessageFilter());
break;
default:
break;

View File

@@ -26,6 +26,7 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.p25.identifier.channel.StandardChannel;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.ISourceEventListener;
@@ -100,9 +101,8 @@ public abstract class AnalogDecoderState extends DecoderState implements ISource
if(mDecodeEvent == null)
{
mDecodeEvent = DecodeEvent.builder(System.currentTimeMillis())
mDecodeEvent = DecodeEvent.builder(DecodeEventType.CALL, System.currentTimeMillis())
.channel(mChannelDescriptor)
.eventDescription("CALL")
.details(getDecoderType().name())
.identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers()))
.build();

View File

@@ -22,45 +22,55 @@ package io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* Message filter for Digital Coded Squelch (DCS)
*/
public class DCSMessageFilter extends Filter<IMessage>
public class DCSMessageFilter extends Filter<IMessage,String>
{
private Map<String,FilterElement<String>> mElements = new HashMap<>();
private static final String DCS_MESSAGE_FILTER_TAG = "DCS";
private static final String DCS_KEY = "DCS";
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public DCSMessageFilter()
{
super("DCS Message Filter");
mElements.put(DCS_MESSAGE_FILTER_TAG, new FilterElement<>("DCS Code", true));
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled();
}
return false;
super("DCS Messages");
add(new FilterElement(DCS_KEY));
}
/**
* Indicates that this filter can process DCS messages.
* @param message to test
* @return true if the message can be processed
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DCSMessage;
return message instanceof DCSMessage && super.canProcess(message);
}
/**
* Key extractor that always returns the same key constant.
* @return extractor function.
*/
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, String> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
return DCS_KEY;
}
}
}

View File

@@ -286,9 +286,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
DecodeEvent smsEvent = DMRDecodeEvent.builder(sms.getTimestamp())
.eventType(DecodeEventType.SMS)
.eventDescription(DecodeEventType.SMS.toString())
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp())
.details("MESSAGE: " + sms.getSMS())
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
.timeslot(sms.getTimeslot())
@@ -312,9 +310,7 @@ public class DMRDecoderState extends TimeslotDecoderState
mic.update(hyteraSmsPacket.getSource());
mic.update(hyteraSmsPacket.getDestination());
DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.SMS.name())
.eventType(DecodeEventType.DATA_PACKET)
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("SMS:" + hyteraSmsPacket.getSMS())
@@ -326,9 +322,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.UNKNOWN_PACKET.name())
.eventType(DecodeEventType.DATA_PACKET)
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
@@ -337,9 +331,7 @@ public class DMRDecoderState extends TimeslotDecoderState
}
else
{
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.DATA_PACKET.name())
.eventType(DecodeEventType.DATA_PACKET)
DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.timeslot(packet.getTimeslot())
.details(packet.toString())
@@ -350,9 +342,8 @@ public class DMRDecoderState extends TimeslotDecoderState
GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket());
if (geoPosition != null) {
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp())
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, packet.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.protocol(Protocol.LRRP)
.location(geoPosition)
@@ -784,9 +775,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_REGISTRATION_REQUEST:
DecodeEvent event = DMRDecodeEvent.builder(csbk.getTimestamp())
DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp())
.details("Registration Request")
.eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -794,9 +784,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_REGISTRATION_RESPONSE:
DecodeEvent regRespEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp())
.details("Registration Response")
.eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -837,9 +826,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION:
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp())
.details("TALKGROUP AFFILIATION")
.eventDescription(DecodeEventType.AFFILIATE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -853,20 +841,16 @@ public class DMRDecoderState extends TimeslotDecoderState
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
return DMRDecodeEvent.builder(csbk.getTimestamp())
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
.channel(channel)
.eventType(decodeEventType)
.details(csbk.getOpcode().getLabel())
.eventDescription(decodeEventType.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
return DMRDecodeEvent.builder(csbk.getTimestamp())
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(details)
@@ -1119,8 +1103,7 @@ public class DMRDecoderState extends TimeslotDecoderState
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.update(gpsInformation.getGPSLocation());
DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp())
.eventDescription(DecodeEventType.GPS.toString())
DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp())
.identifiers(ic)
.timeslot(message.getTimeslot())
.details("LOCATION:" + gpsInformation.getGPSLocation())
@@ -1151,9 +1134,8 @@ public class DMRDecoderState extends TimeslotDecoderState
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = DMRDecodeEvent.builder(timestamp)
mCurrentCallEvent = DMRDecodeEvent.builder(type, timestamp)
.channel(getCurrentChannel())
.eventDescription(type.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -1164,7 +1146,6 @@ public class DMRDecoderState extends TimeslotDecoderState
{
if(type != DecodeEventType.CALL)
{
mCurrentCallEvent.setEventDescription(type.toString());
mCurrentCallEvent.setDetails(details);
}

View File

@@ -329,10 +329,8 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
if(isStale(event, timestamp, identifierCollection)) //Create new event
{
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -351,11 +349,9 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Continue")
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.details("CONTINUE - CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -392,15 +388,6 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
{
if(event.getEventDescription() == null)
{
event.setEventDescription(decodeEventType + IGNORED);
}
else if(!event.getEventDescription().endsWith(IGNORED))
{
event.setEventDescription(event.getEventDescription() + IGNORED);
}
if(event.getDetails() == null)
{
event.setDetails(DATA_CALL_IGNORED);

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.event;
@@ -34,11 +33,12 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
/**
* Constructs an instance
* @param decodeEventType for the event
* @param timestamp for the event
*/
public DMRChannelGrantEvent(long timestamp)
public DMRChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
super(timestamp);
super(decodeEventType, timestamp);
}
/**
@@ -63,9 +63,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(long timeStart)
public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(DecodeEventType decodeEventType, long timeStart)
{
return new DMRChannelGrantDecodeEventBuilder(timeStart);
return new DMRChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -76,7 +76,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
protected long mTimeStart;
protected long mDuration;
protected DecodeEventType mDecodeEventType;
protected String mEventDescription;
protected IdentifierCollection mIdentifierCollection;
protected DMRChannel mChannel;
protected String mDetails;
@@ -87,8 +86,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
*
* @param timeStart
*/
public DMRChannelGrantDecodeEventBuilder(long timeStart)
public DMRChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -131,25 +131,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
return this;
}
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public DMRChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public DMRChannelGrantDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -176,13 +157,11 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
*/
public DMRChannelGrantEvent build()
{
DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mTimeStart);
DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannel);
decodeEvent.setTimeslot(mChannel.getTimeslot());
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,12 +14,13 @@
*
* 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.dmr.event;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
@@ -32,9 +32,9 @@ public class DMRDecodeEvent extends DecodeEvent
* Constucts a decode event
* @param start
*/
public DMRDecodeEvent(long start)
public DMRDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.DMR);
}
@@ -43,9 +43,9 @@ public class DMRDecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.DMR);
return decodeEventBuilder;
}

View File

@@ -39,7 +39,7 @@ public enum Opcode
STANDARD_CHANNEL_TIMING(Vendor.STANDARD, 7, "CHANNEL TIMING"),
STANDARD_ALOHA(Vendor.STANDARD, 25, "ALOHA"),
STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER(Vendor.STANDARD, 26, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT INBOUND HEADER"),
STANDARD_AHOY(Vendor.STANDARD, 28, "AHOY"),
STANDARD_ACTIVATION(Vendor.STANDARD, 30, "ACTIVATION"),
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST(Vendor.STANDARD, 31, "RANDOM ACCESS SERVICE REQUEST"),
@@ -102,13 +102,18 @@ public enum Opcode
HYTERA_68_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA 68 XPT PREAMBLE"),
HYTERA_68_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA 68 CSBKO 62"),
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");
private Vendor mVendor;
private int mValue;
private String mLabel;
/**
* Constructor
* @param vendor for the opcode
* @param value for the opcode
* @param label to display for the opcode
*/
Opcode(Vendor vendor, int value, String label)
{
mVendor = vendor;
@@ -151,19 +156,86 @@ public enum Opcode
*/
public boolean isDataChannelGrantOpcode()
{
return DATA_CHANNEL_GRANT_OPCODES.contains(this);
return DATA_CHANNEL_GRANTS.contains(this);
}
/**
* Data Channel Grant opcodes
* Data channel grant opcodes
*/
public static final EnumSet<Opcode> DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM,
STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM,
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
public static final EnumSet<Opcode> DATA_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM, STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM, STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
/**
* Data opcodes
*/
public static final EnumSet<Opcode> DATA_OPCODES = EnumSet.of(STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER,
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER, STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_OUTBOUND_HEADER,
STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_INBOUND_HEADER, STANDARD_PREAMBLE);
/**
* Mobile request and response opcodes
*/
public static final EnumSet<Opcode> MOBILE_REQUEST_RESPONSE = EnumSet.of(STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST,
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST);
/**
* Network request and response and announcement opcodes
*/
public static final EnumSet<Opcode> NETWORK_REQUEST_RESPONSE = EnumSet.of(STANDARD_FEATURE_NOT_SUPPORTED,
STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_RESPONSE, STANDARD_AHOY, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC,
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_TSCC, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD,
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_PAYLOAD, STANDARD_NEGATIVE_ACKNOWLEDGE_RESPONSE, STANDARD_CLEAR,
STANDARD_MOVE_TSCC, STANDARD_CHANNEL_TIMING,
STANDARD_ALOHA, STANDARD_ACTIVATION, STANDARD_ANNOUNCEMENT, STANDARD_MAINTENANCE, STANDARD_PROTECT);
/**
* Voice channel grant opcodes
*/
public static final EnumSet<Opcode> VOICE_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_VOICE_CHANNEL_GRANT,
STANDARD_TALKGROUP_VOICE_CHANNEL_GRANT, STANDARD_BROADCAST_TALKGROUP_VOICE_CHANNEL_GRANT,
STANDARD_DUPLEX_PRIVATE_VOICE_CHANNEL_GRANT);
/**
* Hytera opcodes
*/
public static final EnumSet<Opcode> HYTERA = EnumSet.of(HYTERA_08_ACKNOWLEDGE, HYTERA_08_ANNOUNCEMENT,
HYTERA_08_CSBKO_44, HYTERA_08_CSBKO_47, HYTERA_68_XPT_SITE_STATE, HYTERA_68_ALOHA,
HYTERA_68_ACKNOWLEDGE, HYTERA_68_ANNOUNCEMENT, HYTERA_68_XPT_PREAMBLE, HYTERA_68_CSBKO_62);
/**
* Motorola Capacity Max opcodes
*/
public static final EnumSet<Opcode> MOTOROLA_CAPACITY_MAX = EnumSet.of(MOTOROLA_CAPMAX_ALOHA);
/**
* Motorola Capacity Plus opcodes
*/
public static final EnumSet<Opcode> MOTOROLA_CAPACITY_PLUS = EnumSet.of(MOTOROLA_CAPPLUS_CALL_ALERT,
MOTOROLA_CAPPLUS_CALL_ALERT_ACK, MOTOROLA_CAPPLUS_DATA_WINDOW_ANNOUNCEMENT,
MOTOROLA_CAPPLUS_DATA_WINDOW_GRANT, MOTOROLA_CAPPLUS_NEIGHBOR_REPORT, MOTOROLA_CAPPLUS_CSBKO_60,
MOTOROLA_CAPPLUS_PREAMBLE, MOTOROLA_CAPPLUS_SITE_STATUS);
/**
* Motorola Connect Plus opcodes
*/
public static final EnumSet<Opcode> MOTOROLA_CONNECT_PLUS = EnumSet.of(MOTOROLA_CONPLUS_NEIGHBOR_REPORT,
MOTOROLA_CONPLUS_VOICE_CHANNEL_USER, MOTOROLA_CONPLUS_DATA_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_10,
MOTOROLA_CONPLUS_TERMINATE_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_16, MOTOROLA_CONPLUS_REGISTRATION_REQUEST,
MOTOROLA_CONPLUS_REGISTRATION_RESPONSE, MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION,
MOTOROLA_CONPLUS_DATA_WINDOW_ANNOUNCEMENT, MOTOROLA_CONPLUS_DATA_WINDOW_GRANT);
/**
* Indicates if the opcode is included in one of the enumset groupings above
* @return
*/
public boolean isGrouped()
{
return DATA_CHANNEL_GRANTS.contains(this) || DATA_OPCODES.contains(this) ||
MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_REQUEST_RESPONSE.contains(this) ||
VOICE_CHANNEL_GRANTS.contains(this) || HYTERA.contains(this) || MOTOROLA_CAPACITY_MAX.contains(this) ||
MOTOROLA_CAPACITY_PLUS.contains(this) || MOTOROLA_CONNECT_PLUS.contains(this);
}
/**
* Lookup map of vendors and opcode value to opcodes

View File

@@ -67,14 +67,14 @@ public enum LCOpcode
//CAP+ SLCO 15 = Rest Channel https://forums.radioreference.com/threads/understanding-capacity-plus-trunking.209318/page-7
SHORT_CAPACITY_PLUS_REST_CHANNEL_NOTIFICATION(Vendor.MOTOROLA_CAPACITY_PLUS, false,15, "REST CHANNEL NOTIFICATION"),
SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "HYTERA XPT CHANNEL"),
SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "STANDARD XPT CHANNEL"),
SHORT_HYTERA_XPT_CHANNEL(Vendor.HYTERA_68, false, 8, "HYTERA XPT CHANNEL"),
SHORT_CONNECT_PLUS_TRAFFIC_CHANNEL(Vendor.STANDARD, false, 9, "TRAFFIC CHANNEL INFO"),
SHORT_CONNECT_PLUS_CONTROL_CHANNEL(Vendor.STANDARD, false, 10, "CONTROL CHANNEL INFO"),
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "SHORT UNKNOWN");
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "UNKNOWN");
private static final EnumSet<LCOpcode> TALKER_ALIAS_OPCODES = EnumSet.of(FULL_STANDARD_TALKER_ALIAS_HEADER,
FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3);

View File

@@ -63,7 +63,6 @@ public class PacketSequenceMessageFactory
if(packetSequence.hasPacketSequenceHeader())
{
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
boolean confirmed = primaryHeader.isConfirmedData();
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
if(packet != null)

View File

@@ -0,0 +1,74 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
import java.util.Collection;
import java.util.function.Function;
/**
* Filter for opcode based control (CSBK) messages.
*/
public class ControlMessageFilter extends Filter<IMessage, Opcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
* @param name of this control message filter
* @param opcodes to use with this filter
*/
public ControlMessageFilter(String name, Collection<Opcode> opcodes)
{
super(name);
for(Opcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
@Override
public Function<IMessage, Opcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,Opcode>
{
@Override
public Opcode apply(IMessage message)
{
if(message instanceof CSBKMessage csbk)
{
return csbk.getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for DMR control (CSBK) messages using Opcodes for filter elements.
*/
public class ControlMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public ControlMessageFilterSet()
{
super("Control (CSBK) Messages");
addFilter(new ControlMessageFilter("Data Channel Grants", Opcode.DATA_CHANNEL_GRANTS));
addFilter(new ControlMessageFilter("Data-Related", Opcode.DATA_OPCODES));
addFilter(new ControlMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE));
addFilter(new ControlMessageFilter("Network Request/Response", Opcode.NETWORK_REQUEST_RESPONSE));
addFilter(new ControlMessageFilter("Voice Channel Grants", Opcode.VOICE_CHANNEL_GRANTS));
addFilter(new ControlMessageFilter("Hytera", Opcode.HYTERA));
addFilter(new ControlMessageFilter("Motorola Capacity Max", Opcode.MOTOROLA_CAPACITY_MAX));
addFilter(new ControlMessageFilter("Motorola Capacity Plus", Opcode.MOTOROLA_CAPACITY_PLUS));
addFilter(new ControlMessageFilter("Motorola Connect Plus", Opcode.MOTOROLA_CONNECT_PLUS));
//Add all remaining opcodes that are not grouped into one of the above enumsets.
List<Opcode> otherOpcodes = new ArrayList<>();
for(Opcode opcode: Opcode.values())
{
if(!opcode.isGrouped())
{
otherOpcodes.add(opcode);
}
}
addFilter(new ControlMessageFilter("Other/Unknown", otherOpcodes));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof CSBKMessage && super.canProcess(message);
}
}

View File

@@ -0,0 +1,80 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.DataType;
import java.util.function.Function;
/**
* Filter for data messages, excluding CSBK messages.
*/
public class DataMessageFilter extends Filter<IMessage, DataType>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public DataMessageFilter()
{
super("Data Messages");
for(DataType dataType: DataType.values())
{
add(new FilterElement<>(dataType));
}
}
@Override
public Function<IMessage, DataType> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DataMessage && !(message instanceof CSBKMessage) && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,DataType>
{
@Override
public DataType apply(IMessage message)
{
if(message instanceof DataMessage dataMessage)
{
return dataMessage.getSlotType().getDataType();
}
return null;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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
@@ -17,31 +17,29 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter.lastheard;
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.module.ModuleEventBusMessage;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.filter.SyncLossMessageFilter;
import io.github.dsheirer.message.IMessage;
/**
* Response to decode event history request from a processing chain for the specified channel.
* Filter set for DMR messages
*/
public class LastHeardHistoryResponse extends ModuleEventBusMessage
public class DmrMessageFilterSet extends FilterSet<IMessage>
{
private LastHeardHistory mLastHeardHistory;
/**
* Constructs an instance
* @param history that has a decode event history
* Constructor
*/
public LastHeardHistoryResponse(LastHeardHistory history)
public DmrMessageFilterSet()
{
mLastHeardHistory = history;
}
/**
* Decode event history module
*/
public LastHeardHistory getDecodeEventHistory()
{
return mLastHeardHistory;
super("DMR Messages");
addFilter(new ControlMessageFilterSet());
addFilter(new VoiceMessageFilter());
addFilter(new DataMessageFilter());
addFilter(new DmrPacketSequenceFilter());
addFilter(new LinkControlMessageFilterSet());
addFilter(new SyncLossMessageFilter());
addFilter(new DmrOtherMessageFilter());
}
}

View File

@@ -0,0 +1,73 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
import java.util.function.Function;
/**
* Filter for unknown messages, meaning messages not handled by any other filter.
*/
public class DmrOtherMessageFilter extends Filter<IMessage,String>
{
private static final String OTHER_KEY = "Other/Unknown Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public DmrOtherMessageFilter()
{
super("Other DMR messages");
add(new FilterElement<>(OTHER_KEY));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DMRMessage && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof DMRMessage)
{
return OTHER_KEY;
}
return null;
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraUnknownPacket;
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
import java.util.function.Function;
/**
* Filter for DMR packet sequence messages
*/
public class DmrPacketSequenceFilter extends Filter<IMessage,String>
{
private static final String KEY_ARS = "Automatic Registration Service (ARS)";
private static final String KEY_HYTERA_SMS = "Hytera Short Message Service (SMS)";
private static final String KEY_HYTERA_UNKNOWN = "Hytera Unknown";
private static final String KEY_LRRP = "Location Request/Response Protocol (LRRP)";
private static final String KEY_UDT_SMS = "Unified Data Transport - Short Message Service";
private static final String KEY_XCMP = "Extensible Command Message Protocol (XCMP)";
private static final String KEY_UNKNOWN = "Other/Unknown";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*/
public DmrPacketSequenceFilter()
{
super("Packet Sequence Messages");
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DMRPacketMessage && super.canProcess(message);
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof UDTShortMessageService)
{
return KEY_UDT_SMS;
}
else if(message instanceof DMRPacketMessage packetMessage)
{
if(packetMessage.getPacket() instanceof ARSPacket)
{
return KEY_ARS;
}
else if(packetMessage.getPacket() instanceof LRRPPacket)
{
return KEY_LRRP;
}
else if(packetMessage.getPacket() instanceof XCMPPacket)
{
return KEY_XCMP;
}
else if(packetMessage.getPacket() instanceof HyteraSmsPacket)
{
return KEY_HYTERA_SMS;
}
else if(packetMessage.getPacket() instanceof HyteraUnknownPacket)
{
return KEY_HYTERA_UNKNOWN;
}
//TODO: finish this
}
return KEY_UNKNOWN;
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
import java.util.Collection;
import java.util.function.Function;
/**
* Message filter for link control opcodes.
*
* Note: this does not include link control messages carried by data messages with embedded link control.
*/
public class LinkControlMessageFilter extends Filter<IMessage, LCOpcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public LinkControlMessageFilter(String name, Collection<LCOpcode> opcodes)
{
super(name);
for(LCOpcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
@Override
public Function<IMessage, LCOpcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LCOpcode>
{
@Override
public LCOpcode apply(IMessage message)
{
if(message instanceof LCMessage lcMessage)
{
return lcMessage.getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for short & voice-fragment link control messages
*
* Note: this filter set does NOT work for link control messages in Data Message with Link Control.
*/
public class LinkControlMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public LinkControlMessageFilterSet()
{
super("Short/Voice-Fragment Link Control Messages");
List<LCOpcode> fullLinkControl = new ArrayList<>();
List<LCOpcode> shortLinkControl = new ArrayList<>();
for(LCOpcode opcode: LCOpcode.values())
{
if(opcode.isFull())
{
fullLinkControl.add(opcode);
}
else
{
shortLinkControl.add(opcode);
}
}
addFilter(new LinkControlMessageFilter("Full Link Control", fullLinkControl));
addFilter(new LinkControlMessageFilter("Short Link Control", shortLinkControl));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LCMessage && super.canProcess(message);
}
}

View File

@@ -0,0 +1,86 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
import java.util.function.Function;
/**
* Filter for voice messages
*/
public class VoiceMessageFilter extends Filter<IMessage, DMRSyncPattern>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*/
public VoiceMessageFilter()
{
super("Voice Messages");
add(new FilterElement<>(DMRSyncPattern.BASE_STATION_VOICE));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_B));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_C));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_D));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_E));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_F));
add(new FilterElement<>(DMRSyncPattern.MOBILE_STATION_VOICE));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_B));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_C));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_D));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_E));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_F));
add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_1));
add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_2));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof VoiceMessage && super.canProcess(message);
}
@Override
public Function<IMessage,DMRSyncPattern> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,DMRSyncPattern>
{
@Override
public DMRSyncPattern apply(IMessage message)
{
if(message instanceof VoiceMessage voice)
{
return voice.getSyncPattern();
}
return null;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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
@@ -27,18 +27,18 @@ public enum DataType
VOICE_HEADER(1, 288, "VOICE HEADER"),
TLC(2, 288, "TERMINATOR"),
CSBK(3, 288, "CSBK"),
MBC_HEADER(4, 288, "MBC HEADER"),
MBC_BLOCK(5, 276, "MBC"),
MBC_HEADER(4, 288, "MULTI-BLOCK CONTROL HEADER"),
MBC_BLOCK(5, 276, "MULTI-BLOCK CONTROL BLOCK"),
DATA_HEADER(6, 288, "DATA HEADER"),
RATE_1_OF_2_DATA(7, 276, "RATE 1/2 PACKET"),
RATE_3_OF_4_DATA(8, 276, "RATE 3/4 PACKET"),
SLOT_IDLE(9, 276, "IDLE"),
RATE_1_DATA(10, 276, "RATE 1/1 PACKET"),
USB_DATA(11, 276, "UNIFIED SINGLE BLOCK DATA"),
MBC_ENC_HEADER(12, 276, "MBC ENCRYPTED HEADER"),
MBC_ENC_HEADER(12, 276, "MULTI-BLOCK CONTROL ENCRYPTED HEADER"),
DATA_ENC_HEADER(13, 276, "DATA ENCRYPTED HEADER"),
CHANNEL_CONTROL_ENC_HEADER(14, 276, "CONTROL CHANNEL ENCRYPTED HEADER"),
RESERVED_15(15, -1, "RESERVED"),
RESERVED_15(15, -1, "RESERVED 15"),
UNKNOWN(-1, -1, "UNKNOWN");
private int mValue;

View File

@@ -0,0 +1,125 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
import java.awt.EventQueue;
import java.util.LinkedList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
/**
* AbstractTableModel implementation supporting clearable method options.
*/
public abstract class ClearableHistoryModel<T> extends AbstractTableModel
{
public static final int DEFAULT_HISTORY_SIZE = 200;
private LinkedList<T> mItems = new LinkedList<>();
private int mHistorySize = DEFAULT_HISTORY_SIZE;
/**
* Access an item/row by the model index value.
* @param index to retrieve
* @return item or null
*/
public T getItem(int index)
{
if(index < mItems.size())
{
return mItems.get(index);
}
return null;
}
/**
* Adds the item to the top of the item list and removes any tail items while the item list size exceeds the
* maximum history size for this model.
* @param item to add
*/
public void add(T item)
{
mItems.addFirst(item);
ClearableHistoryModel.this.fireTableRowsInserted(0, 0);
while(mItems.size() > mHistorySize)
{
mItems.removeLast();
super.fireTableRowsDeleted(mItems.size() - 1, mItems.size() - 1);
}
}
/**
* Clears all messages from history
*/
public void clear()
{
EventQueue.invokeLater(() -> {
mItems.clear();
fireTableDataChanged();
});
}
/**
* Clears the current messages and loads the messages argument
*/
public void clearAndSet(List<T> items)
{
EventQueue.invokeLater(() -> {
mItems.clear();
fireTableDataChanged();
for(T item: items)
{
add(item);
}
});
}
/**
* Current history size
* @return history size
*/
public int getHistorySize()
{
return mHistorySize;
}
/**
* Resets the history size to the default value.
*/
public void resetHistorySize()
{
setHistorySize(DEFAULT_HISTORY_SIZE);
}
/**
* Sets the history size
* @param historySize
*/
public void setHistorySize(int historySize)
{
mHistorySize = historySize;
}
@Override
public int getRowCount()
{
return mItems.size();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -32,27 +32,33 @@ public class DecodeEvent implements IDecodeEvent
{
private long mTimeStart;
private long mTimeEnd;
private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
private String mDetails;
private Protocol mProtocol;
private Integer mTimeslot;
private int mTimeslot = -1;
public DecodeEvent(long start)
/**
* Constructs an instance
* @param decodeEventType is mandatory to support event filtering by event type.
* @param start time of the event.
*/
public DecodeEvent(DecodeEventType decodeEventType, long start)
{
mDecodeEventType = decodeEventType;
mTimeStart = start;
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param decodeEventType for the event
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
return new DecodeEventBuilder(timeStart);
return new DecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -115,23 +121,6 @@ public class DecodeEvent implements IDecodeEvent
mTimeEnd = mTimeStart + duration;
}
/**
* Event description
*/
@Override
public String getEventDescription()
{
return mEventDescription;
}
/**
* Sets the event description text
*/
public void setEventDescription(String description)
{
mEventDescription = description;
}
/**
* {@link DecodeEventType}
*/
@@ -140,13 +129,6 @@ public class DecodeEvent implements IDecodeEvent
return mDecodeEventType;
}
/**
* Sets the {@link DecodeEventType}
*/
public void setEventType(DecodeEventType eventType) {
this.mDecodeEventType = eventType;
}
/**
* Identifier collection for this event.
*/
@@ -222,24 +204,24 @@ public class DecodeEvent implements IDecodeEvent
* @return timeslot or default value of 0
*/
@Override
public Integer getTimeslot()
public int getTimeslot()
{
return mTimeslot;
}
/**
* Indicates if this event specifies a timeslot
* Indicates if this event specifies a timeslot that is not negative
*/
public boolean hasTimeslot()
{
return mTimeslot != null;
return mTimeslot >= 0;
}
/**
* Sets the timeslot for this event
* @param timeslot of the event
*/
public void setTimeslot(Integer timeslot)
public void setTimeslot(int timeslot)
{
mTimeslot = timeslot;
}
@@ -249,7 +231,7 @@ public class DecodeEvent implements IDecodeEvent
{
StringBuilder sb = new StringBuilder();
sb.append(mProtocol);
sb.append(" DECODE EVENT: ").append(getEventDescription());
sb.append(" DECODE EVENT: ").append(getEventType());
sb.append(" DETAILS:").append(getDetails());
if(mIdentifierCollection != null)
{
@@ -267,19 +249,19 @@ public class DecodeEvent implements IDecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
protected String mDetails;
protected Protocol mProtocol = Protocol.UNKNOWN;
protected Integer mTimeslot;
protected int mTimeslot = -1;
/**
* Constructs a builder instance with the specified start time in milliseconds
*/
public DecodeEventBuilder(long timeStart)
public DecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -313,25 +295,6 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public DecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public DecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -363,7 +326,12 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
public DecodeEventBuilder timeslot(Integer timeslot)
/**
* Sets the timeslot for this event
* @param timeslot
* @return
*/
public DecodeEventBuilder timeslot(int timeslot)
{
mTimeslot = timeslot;
return this;
@@ -374,12 +342,10 @@ public class DecodeEvent implements IDecodeEvent
*/
public DecodeEvent build()
{
DecodeEvent decodeEvent = new DecodeEvent(mTimeStart);
DecodeEvent decodeEvent = new DecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setTimeslot(mTimeslot);

View File

@@ -21,25 +21,20 @@ package io.github.dsheirer.module.decode.event;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.awt.EventQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
public class DecodeEventModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
/**
* Decode event table model
*/
public class DecodeEventModel extends ClearableHistoryModel<IDecodeEvent> implements Listener<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventModel.class);
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
@@ -50,12 +45,6 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
public static final int COLUMN_CHANNEL = 7;
public static final int COLUMN_FREQUENCY = 8;
public static final int COLUMN_DETAILS = 9;
protected int mMaxMessages = 200;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mEventFilterSet = new DecodeEventFilterSet();
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public DecodeEventModel()
@@ -70,72 +59,10 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT)
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_TIME);
}
fireTableDataChanged();
}
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_FROM_ID);
fireTableCellUpdated(row, COLUMN_TO_ID);
}
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
{
it.remove();
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
@@ -146,49 +73,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
*/
public void receive(final IDecodeEvent event)
{
//Disabled until #1368 decode event is fully implemented
// if (!mEventFilterSet.passes(event))
// {
// return;
// }
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mEventFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mEventFilterSet = filterSet;
}
@Override
public int getRowCount()
{
return mEvents.size();
EventQueue.invokeLater(() -> add(event));
}
@Override
@@ -205,58 +90,55 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
IDecodeEvent event = getItem(rowIndex);
if(event != null)
{
IDecodeEvent event = mEvents.get(rowIndex);
if(event != null)
switch(columnIndex)
{
switch(columnIndex)
{
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventDescription();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventType().getLabel();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
if(channelDescriptor != null)
if(channelDescriptor != null)
{
if(event.hasTimeslot())
{
if(event.hasTimeslot())
{
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
}
else
{
return channelDescriptor.toString();
}
return channelDescriptor + " TS:" + event.getTimeslot();
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
return channelDescriptor.toString();
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -25,7 +25,6 @@ import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
@@ -33,10 +32,7 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
@@ -58,7 +54,10 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain>
{
@@ -75,7 +74,10 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private EventManagementPanel mEventManagementPanel;
private FilterSet<IDecodeEvent> mFilterSet = new DecodeEventFilterSet();
private TableRowSorter<TableModel> mTableRowSorter;
private HistoryManagementPanel<IDecodeEvent> mHistoryManagementPanel;
/**
* View for call event table
@@ -91,17 +93,20 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mTable = new JTable(mEventModel);
mTable.setAutoCreateRowSorter(true);
mTableRowSorter = new TableRowSorter<>(mEventModel);
mTableRowSorter.setRowFilter(new EventRowFilter());
mTable.setRowSorter(mTableRowSorter);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mEventManagementPanel = new EventManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
//Disabled until #1368 decode event is fully implemented
// add(mEventManagementPanel, "span,growx");
mHistoryManagementPanel = new HistoryManagementPanel<>(mEventModel, "Event Filter Editor");
mHistoryManagementPanel.updateFilterSet(mFilterSet);
add(mHistoryManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
//Register filter change listener to refresh the table any time the event filters are changed.
mFilterSet.register(() -> mEventModel.fireTableDataChanged());
}
public void dispose()
@@ -124,22 +129,14 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
private void updateCellRenderers()
{
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
.setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
.setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
.setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME).setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION).setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID).setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS).setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID).setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS).setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL).setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY).setCellRenderer(new FrequencyCellRenderer());
}
@Override
@@ -156,76 +153,17 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mEventModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mEventModel);
mEventManagementPanel.enableButtons();
mHistoryManagementPanel.setEnabled(true);
}
else
{
mCurrentEventHistory = null;
mEventModel.clearAndSet(Collections.emptyList());
mEventManagementPanel.disableButtons();
mHistoryManagementPanel.setEnabled(false);
}
});
}
public class EventManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public EventManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Message Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((DecodeEventModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((DecodeEventModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((DecodeEventModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
@@ -479,4 +417,26 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
setHorizontalAlignment(JLabel.CENTER);
}
}
/**
* Row filter for decode events
*/
public class EventRowFilter extends RowFilter<TableModel, Integer>
{
@Override
public boolean include(Entry<? extends TableModel, ? extends Integer> entry)
{
if(entry.getModel() instanceof DecodeEventModel model)
{
IDecodeEvent event = model.getItem(entry.getIdentifier());
if(event != null)
{
return mFilterSet.canProcess(event) && mFilterSet.passes(event);
}
}
return false;
}
}
}

View File

@@ -14,11 +14,17 @@
*
* 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.event;
import java.util.Arrays;
import java.util.EnumSet;
/**
* Enumeration of event types for decoded events.
*/
public enum DecodeEventType
{
AFFILIATE("Affiliate"),
@@ -43,6 +49,7 @@ public enum DecodeEventType
CALL_UNIT_TO_UNIT_ENCRYPTED("Encrypted Unit To Unit Call"),
CALL_NO_TUNER("Call - No Tuner"),
CALL_TIMEOUT("Call Timeout"),
CELLOCATOR("Cellocator"),
COMMAND("Command"),
DATA_CALL("Data Call"),
DATA_CALL_ENCRYPTED("Encrypted Data Call"),
@@ -55,6 +62,7 @@ public enum DecodeEventType
ID_ANI("ANI"),
ID_UNIQUE("Unique ID"),
IP_PACKET("IP Packet"),
LRRP("Motorola LRRP"),
NOTIFICATION("Notification"),
PAGE("Page"),
QUERY("Query"),
@@ -74,16 +82,82 @@ public enum DecodeEventType
private String mLabel;
/**
* Encrypted voice call event types for filtering
*/
public static final EnumSet<DecodeEventType> VOICE_CALLS_ENCRYPTED = EnumSet.of(DecodeEventType.CALL_ENCRYPTED,
DecodeEventType.CALL_GROUP_ENCRYPTED, DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED);
/**
* Voice call event types for filtering
*/
public static final EnumSet<DecodeEventType> VOICE_CALLS = EnumSet.of(DecodeEventType.CALL_GROUP,
DecodeEventType.CALL_PATCH_GROUP, DecodeEventType.CALL_ALERT, DecodeEventType.CALL_DETECT,
DecodeEventType.CALL_DO_NOT_MONITOR, DecodeEventType.CALL_END, DecodeEventType.CALL_INTERCONNECT,
DecodeEventType.CALL_UNIQUE_ID, DecodeEventType.CALL_UNIT_TO_UNIT, DecodeEventType.CALL_NO_TUNER,
DecodeEventType.CALL_TIMEOUT);
/**
* Command event types for filtering
*/
public static final EnumSet<DecodeEventType> COMMANDS = EnumSet.of(DecodeEventType.ANNOUNCEMENT,
DecodeEventType.STATION_ID, DecodeEventType.ACKNOWLEDGE, DecodeEventType.PAGE, DecodeEventType.QUERY,
DecodeEventType.RADIO_CHECK, DecodeEventType.STATUS, DecodeEventType.COMMAND, DecodeEventType.EMERGENCY,
DecodeEventType.NOTIFICATION, DecodeEventType.FUNCTION);
/**
* Data call event types for filtering
*/
public static final EnumSet<DecodeEventType> DATA_CALLS = EnumSet.of(DecodeEventType.DATA_CALL,
DecodeEventType.DATA_CALL_ENCRYPTED, DecodeEventType.DATA_PACKET, DecodeEventType.GPS,
DecodeEventType.IP_PACKET, DecodeEventType.UDP_PACKET, DecodeEventType.SDM, DecodeEventType.ID_ANI,
DecodeEventType.ID_UNIQUE);
/**
* Registration event types for filtering
*/
public static final EnumSet<DecodeEventType> REGISTRATION = EnumSet.of(DecodeEventType.AFFILIATE,
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, DecodeEventType.REGISTER, DecodeEventType.REGISTER_ESN,
DecodeEventType.DEREGISTER, DecodeEventType.REQUEST, DecodeEventType.RESPONSE, DecodeEventType.RESPONSE_PACKET);
/**
* All other event types of this enumeration that are not included in the groupings above.
*/
public static final EnumSet<DecodeEventType> OTHERS = EnumSet.copyOf(Arrays.stream(DecodeEventType.values())
.filter(decodeEventType -> !decodeEventType.isGrouped()).toList());
/**
* Constructor
* @param label for the element
*/
DecodeEventType(String label)
{
mLabel = label;
}
/**
* Indicates if the enumeration element is contained in one of the enumset groupings above.
* @return true if the element is grouped.
*/
public boolean isGrouped()
{
return VOICE_CALLS.contains(this) || VOICE_CALLS_ENCRYPTED.contains(this) || COMMANDS.contains(this) ||
DATA_CALLS.contains(this) || REGISTRATION.contains(this);
}
/**
* Label or pretty value for the element
* @return label
*/
public String getLabel()
{
return mLabel;
}
/**
* Uses label as the default string value.
*/
public String toString()
{
return mLabel;

View File

@@ -0,0 +1,212 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.filter.FilterEditor;
import io.github.dsheirer.filter.FilterSet;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import net.miginfocom.swing.MigLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
/**
* History management panel with controls for managing item histories.
*/
public class HistoryManagementPanel<T> extends JPanel
{
private ClearableHistoryModel mModel;
private FilterSet<T> mFilterSet;
private FilterEditor<T> mFilterEditor;
private JButton mClearButton;
private JButton mFilterButton;
private JSlider mHistorySlider;
private JLabel mHistoryTitleLabel;
private JLabel mHistoryValueLabel;
private String mFilterEditorTitle;
/**
* Constructs an instance
* @param model to manage
*/
public HistoryManagementPanel(ClearableHistoryModel model, String filterEditorTitle)
{
mModel = model;
mFilterEditorTitle = filterEditorTitle;
setLayout(new MigLayout("insets 6 1 5 5", "[]5[]10[]5[]5[][grow]", ""));
add(getFilterButton());
add(getClearButton());
add(getHistoryTitleLabel());
add(getHistorySlider());
add(getHistoryValueLabel());
setEnabled(false);
}
/**
* Updates the filter set
* @param filterSet to use
*/
public void updateFilterSet(FilterSet<T> filterSet)
{
mFilterSet = filterSet;
if(mFilterEditor != null)
{
getFilterEditor().updateFilterSet(filterSet);
}
}
/**
* Overrides the panel method to also set the enabled state for the child controls.
* @param enabled true if this component should be enabled, false otherwise
*/
@Override
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
getClearButton().setEnabled(enabled);
getFilterButton().setEnabled(enabled);
getHistoryValueLabel().setEnabled(enabled);
getHistoryTitleLabel().setEnabled(enabled);
getHistorySlider().setEnabled(enabled);
}
/**
* Filter editor
* @return editor lazily constructed
*/
private FilterEditor<T> getFilterEditor()
{
if(mFilterEditor == null)
{
mFilterEditor = new FilterEditor<>(mFilterEditorTitle, getFilterButton(), mFilterSet);
}
return mFilterEditor;
}
/**
* Filter button for accessing the filter editor
* @return filter button
*/
private JButton getFilterButton()
{
if(mFilterButton == null)
{
mFilterButton = new JButton("Filters");
mFilterButton.setToolTipText("Edit filters");
mFilterButton.addActionListener(arg0 -> EventQueue.invokeLater(() -> getFilterEditor().setVisible(true)));
}
return mFilterButton;
}
/**
* Clear button
* @return clear button
*/
private JButton getClearButton()
{
if(mClearButton == null)
{
mClearButton = new JButton("Clear");
mClearButton.addActionListener(e -> mModel.clear());
mClearButton.setToolTipText("Clears the history");
}
return mClearButton;
}
/**
* History value label.
* @return label
*/
private JLabel getHistoryValueLabel()
{
if(mHistoryValueLabel == null)
{
mHistoryValueLabel = new JLabel(String.valueOf(ClearableHistoryModel.DEFAULT_HISTORY_SIZE));
}
return mHistoryValueLabel;
}
/**
* History title label
* @return label
*/
private JLabel getHistoryTitleLabel()
{
if(mHistoryTitleLabel == null)
{
mHistoryTitleLabel = new JLabel("History:");
}
return mHistoryTitleLabel;
}
/**
* History value slider control
* @return slider
*/
private JSlider getHistorySlider()
{
if(mHistorySlider == null)
{
mHistorySlider = new JSlider();
mHistorySlider.setToolTipText("Adjust history size. Double-click to reset to default 200");
mHistorySlider.setMinimum(0);
mHistorySlider.setMaximum(2000);
mHistorySlider.setMinorTickSpacing(25);
mHistorySlider.setMajorTickSpacing(500);
mHistorySlider.setPaintTicks(false);
mHistorySlider.setPaintLabels(false);
mHistorySlider.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent arg0)
{
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
{
mHistorySlider.setValue(ClearableHistoryModel.DEFAULT_HISTORY_SIZE);
}
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0) {}
});
mHistorySlider.setValue(mModel.getHistorySize());
mHistorySlider.addChangeListener(e -> {
mModel.setHistorySize(mHistorySlider.getValue());
getHistoryValueLabel().setText(String.valueOf(mHistorySlider.getValue()));
});
}
return mHistorySlider;
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -48,11 +48,6 @@ public interface IDecodeEvent
*/
long getDuration();
/**
* A description of the event or event type
*/
String getEventDescription();
/**
* Collection of identifiers associated with the event. This collection should contain a
* Role.FROM and a Role.TO identifier, a Decoder Type identifier, and (optionally) an Alias List
@@ -86,7 +81,7 @@ public interface IDecodeEvent
* Timeslot for the event.
* @return timeslot or default of 0
*/
Integer getTimeslot();
int getTimeslot();
/**
* Indicates if the event has a timeslot specified

View File

@@ -18,19 +18,16 @@
*/
package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.StuffBitsMessage;
import io.github.dsheirer.sample.Listener;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public class MessageActivityModel extends AbstractTableModel implements Listener<IMessage>
/**
* Table Model for decoded IMessages.
*/
public class MessageActivityModel extends ClearableHistoryModel<MessageItem> implements Listener<IMessage>
{
private static final long serialVersionUID = 1L;
private static final int TIME = 0;
@@ -38,95 +35,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
private static final int TIMESLOT = 2;
private static final int MESSAGE = 3;
protected int mMaxMessages = 200;
protected LinkedList<MessageItem> mMessageItems = new LinkedList<>();
protected int[] mColumnWidths = {20, 20, 500};
protected String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
private String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
private SimpleDateFormat mSDFTime = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
private FilterSet<IMessage> mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
/**
* Constructor
*/
public MessageActivityModel()
{
}
/**
* Applies the filter set
* Implements the listener interface and wraps the IMessage in table-compatible message item wrapper.
* @param message to add to the model
*/
public void setFilters(FilterSet filterSet)
{
mMessageFilterSet = filterSet;
}
public void clearFilters()
{
mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
}
/**
* Clears all messages from history
*/
public void clear()
{
EventQueue.invokeLater(() -> {
mMessageItems.clear();
fireTableDataChanged();
});
}
/**
* Clears the current messages and loads the messages argument
*/
public void clearAndSet(List<IMessage> messages)
{
EventQueue.invokeLater(() -> {
mMessageItems.clear();
fireTableDataChanged();
for(IMessage message: messages)
{
receive(message);
}
});
}
public FilterSet<IMessage> getMessageFilterSet()
{
return mMessageFilterSet;
}
public void dispose()
{
mMessageItems.clear();
}
public int[] getColumnWidths()
{
return mColumnWidths;
}
public void setColumnWidths(int[] widths)
{
if(widths.length != 3)
{
throw new IllegalArgumentException("MessageActivityModel - column widths array should have 3 elements");
}
else
{
mColumnWidths = widths;
}
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
public void receive(final IMessage message)
{
//Don't process tail bits or stuff bits message fragments
@@ -135,39 +57,7 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
return;
}
if(mMessageFilterSet.passes(message))
{
final MessageItem messageItem = new MessageItem(message);
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
mMessageItems.addFirst(messageItem);
MessageActivityModel.this.fireTableRowsInserted(0, 0);
prune();
}
});
}
}
private void prune()
{
while(mMessageItems.size() > mMaxMessages)
{
MessageItem removed = mMessageItems.removeLast();
removed.dispose();
super.fireTableRowsDeleted(mMessageItems.size() - 1, mMessageItems.size() - 1);
}
}
@Override
public int getRowCount()
{
return mMessageItems.size();
EventQueue.invokeLater(() -> add(new MessageItem(message)));
}
@Override
@@ -184,20 +74,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(0 <= rowIndex && rowIndex < mMessageItems.size())
{
MessageItem messageItem = mMessageItems.get(rowIndex);
MessageItem item = getItem(rowIndex);
if(item != null)
{
switch(columnIndex)
{
case TIME:
return messageItem.getTimestamp(mSDFTime);
return item.getTimestamp(mSDFTime);
case PROTOCOL:
return messageItem.getProtocol();
return item.getProtocol();
case TIMESLOT:
return messageItem.getTimeslot();
return item.getTimeslot();
case MESSAGE:
return messageItem.getText();
return item.getText();
default:
break;
}

View File

@@ -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,13 +14,10 @@
*
* 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.event;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSplitButton;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageHistory;
@@ -30,37 +26,35 @@ import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.RowFilter;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
/**
* Panel to display decoded messages/activity.
*/
public class MessageActivityPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
private static final String TABLE_PREFERENCE_KEY = "message.activity.panel";
private static MessageActivityModel mMessageModel = new MessageActivityModel();
private static final Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
private final String TABLE_PREFERENCE_KEY = "message.activity.panel";
private MessageActivityModel mMessageModel = new MessageActivityModel();
private MessageHistory mCurrentMessageHistory;
private JTable mTable = new JTable(mMessageModel);
private TableRowSorter<TableModel> mTableRowSorter;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private UserPreferences mUserPreferences;
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
private FilterSet<IMessage> mMessageFilterSet;
private HistoryManagementPanel<IMessage> mHistoryManagementPanel;
/**
* Constructs an instance
@@ -69,13 +63,14 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
public MessageActivityPanel(UserPreferences userPreferences)
{
mUserPreferences = userPreferences;
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
add(mManagementPanel, "span,growx");
add(new JScrollPane(mTable), "span,grow");
mTableRowSorter = new TableRowSorter<>(mMessageModel);
mTableRowSorter.setRowFilter(new MessageRowFilter());
mTable.setRowSorter(mTableRowSorter);
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
mHistoryManagementPanel = new HistoryManagementPanel<>(mMessageModel, "Message Filter Editor");
add(mHistoryManagementPanel, "span,growx");
add(new JScrollPane(mTable), "span,grow");
}
/**
@@ -89,182 +84,62 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
mCurrentMessageHistory.removeListener(mMessageModel);
}
//Unregister from changes made to the filter set
if(mMessageFilterSet != null)
{
mMessageFilterSet.register(null);
}
if(processingChain != null)
{
mCurrentMessageHistory = processingChain.getMessageHistory();
mMessageModel.setFilters(DecoderFactory.getMessageFilters(processingChain.getModules()));
mMessageModel.clearAndSet(mCurrentMessageHistory.getItems());
mMessageFilterSet = DecoderFactory.getMessageFilters(processingChain.getModules());
//Register filter change listener to refresh the table any time the event filters are changed.
mMessageFilterSet.register(() -> mMessageModel.fireTableDataChanged());
if(mHistoryManagementPanel != null)
{
mHistoryManagementPanel.updateFilterSet(mMessageFilterSet);
}
List<MessageItem> currentHistory = new ArrayList<>();
for(IMessage message: mCurrentMessageHistory.getItems())
{
currentHistory.add(new MessageItem(message));
}
mMessageModel.clearAndSet(currentHistory);
mCurrentMessageHistory.addListener(mMessageModel);
mManagementPanel.enableButtons();
mHistoryManagementPanel.setEnabled(true);
}
else
{
mCurrentMessageHistory = null;
mMessageModel.clearFilters();
mMessageFilterSet = null;
mMessageModel.clear();
mManagementPanel.disableButtons();
mHistoryManagementPanel.setEnabled(false);
}
}
public class MessageManagementPanel extends JPanel
/**
* Row visibility filter for messages
*/
public class MessageRowFilter extends RowFilter<TableModel, Integer>
{
private static final long serialVersionUID = 1L;
private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
private MessageFilterButton mFilterButton = new MessageFilterButton();
public MessageManagementPanel()
@Override
public boolean include(Entry<? extends TableModel, ? extends Integer> entry)
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
disableButtons();
add(mFilterButton);
add(mHistoryButton);
}
public void enableButtons()
{
mHistoryButton.setEnabled(true);
mFilterButton.setEnabled(true);
}
public void disableButtons()
{
mHistoryButton.setEnabled(false);
mFilterButton.setEnabled(false);
}
}
public class MessageHistoryButton extends JideSplitButton
{
private static final long serialVersionUID = 1L;
public MessageHistoryButton()
{
super("Clear");
JPanel historyPanel = new JPanel();
historyPanel.add(new JLabel("Message History:"));
final JSlider slider = new JSlider();
slider.setMinimum(0);
slider.setMaximum(2000);
slider.setMajorTickSpacing(500);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addMouseListener(new MouseListener()
if(entry.getModel() instanceof MessageActivityModel model)
{
@Override
public void mouseClicked(MouseEvent arg0)
MessageItem item = model.getItem(entry.getIdentifier());
if(mMessageFilterSet != null && item != null && item.getMessage() != null)
{
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
{
slider.setValue(500); //default
}
IMessage message = item.getMessage();
return mMessageFilterSet.canProcess(message) && mMessageFilterSet.passes(message);
}
}
public void mouseEntered(MouseEvent arg0)
{
}
public void mouseExited(MouseEvent arg0)
{
}
public void mousePressed(MouseEvent arg0)
{
}
public void mouseReleased(MouseEvent arg0)
{
}
});
slider.setValue(((MessageActivityModel) mTable.getModel()).getMaxMessageCount());
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
{
((MessageActivityModel) mTable.getModel()).setMaxMessageCount(slider.getValue());
}
});
historyPanel.add(slider);
add(historyPanel);
/* Clear messages */
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
((MessageActivityModel) mTable.getModel()).clear();
}
});
}
}
public class MessageFilterButton extends JideButton
{
private static final long serialVersionUID = 1L;
public MessageFilterButton()
{
super("Filter");
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
final JFrame editor = new JFrame();
editor.setTitle("Message Filter Editor");
editor.setSize(600, 400);
editor.setLocationRelativeTo(MessageFilterButton.this);
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
editor.setLayout(new MigLayout("", "[grow,fill]",
"[grow,fill][][]"));
@SuppressWarnings("unchecked")
FilterSet<IMessage> filter = (FilterSet<IMessage>) ((MessageActivityModel) mTable.getModel())
.getMessageFilterSet();
FilterEditorPanel<IMessage> panel = new FilterEditorPanel<>(filter);
JScrollPane scroller = new JScrollPane(panel);
scroller.setViewportView(panel);
editor.add(scroller, "wrap");
editor.add(new JLabel("Right-click to select/deselect all nodes"), "wrap");
JButton close = new JButton("Close");
close.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
editor.dispose();
}
});
editor.add(close);
EventQueue.invokeLater(new Runnable()
{
public void run()
{
editor.setVisible(true);
}
});
}
});
return false;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
@@ -31,19 +30,20 @@ public class PlottableDecodeEvent extends DecodeEvent
private double mHeading;
private double mSpeed;
public PlottableDecodeEvent(long start)
public PlottableDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param decodeEventType for the event
* @param timeStart for the event
* @return builder
*/
public static PlottableDecodeEventBuilder plottableBuilder(long timeStart)
public static PlottableDecodeEventBuilder plottableBuilder(DecodeEventType decodeEventType, long timeStart)
{
return new PlottableDecodeEventBuilder(timeStart);
return new PlottableDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -102,7 +102,6 @@ public class PlottableDecodeEvent extends DecodeEvent
{
private long mTimeStart;
private long mDuration;
private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
@@ -115,8 +114,9 @@ public class PlottableDecodeEvent extends DecodeEvent
/**
* Constructs a builder instance with the specified start time in milliseconds
*/
public PlottableDecodeEventBuilder(long timeStart)
public PlottableDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -150,16 +150,6 @@ public class PlottableDecodeEvent extends DecodeEvent
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public PlottableDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -191,15 +181,6 @@ public class PlottableDecodeEvent extends DecodeEvent
return this;
}
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public PlottableDecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/**
* Sets the location for the plottable event
*/
@@ -232,12 +213,10 @@ public class PlottableDecodeEvent extends DecodeEvent
*/
public PlottableDecodeEvent build()
{
PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart);
PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setLocation(mGeoPosition);
@@ -246,5 +225,4 @@ public class PlottableDecodeEvent extends DecodeEvent
return decodeEvent;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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
@@ -17,19 +17,23 @@
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter.lastseen;
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.ModuleEventBusMessage;
import io.github.dsheirer.module.decode.event.DecodeEventType;
/**
* Request for decode event history from a processing chain for the specified channel.
* Event filter for decode event types that are not specified in other event filters.
*
* This is intended as a catch-all for any elements of the DecodeEventType enumeration that may get added in the
* future and are not deliberately added to the various event filter groupings listed in the enumeration.
*/
public class LastSeenHistoryRequest extends ModuleEventBusMessage
public class AllOtherEventFilter extends EventFilter
{
/**
* Constructs an instance
* Constructor
*/
public LastSeenHistoryRequest()
public AllOtherEventFilter()
{
super("Other", DecodeEventType.OTHERS);
}
}

View File

@@ -1,16 +1,40 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent> {
public DecodeEventFilterSet() {
super ("All Messages");
/**
* Top-level decode event filter set.
*/
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent>
{
public DecodeEventFilterSet()
{
super ("All Events");
addFilter(new DecodedCallEventFilter());
addFilter(new DecodedCallEncryptedEventFilter());
addFilter(new DecodedDataEventFilter());
addFilter(new DecodedCommandEventFilter());
addFilter(new DecodedRegistrationEventFilter());
addFilter(new AllOtherEventFilter());
}
}

View File

@@ -1,19 +1,36 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Encrypted call events filter
*/
public class DecodedCallEncryptedEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedCallEncryptedEventFilter()
{
super("Voice Calls - Encrypted", Arrays.asList(
DecodeEventType.CALL_ENCRYPTED,
DecodeEventType.CALL_GROUP_ENCRYPTED,
DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED,
DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED
));
super("Voice Calls - Encrypted", DecodeEventType.VOICE_CALLS_ENCRYPTED);
}
}

View File

@@ -1,25 +1,36 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Call events filter
*/
public class DecodedCallEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedCallEventFilter()
{
super("Voice Calls", Arrays.asList(
DecodeEventType.CALL_GROUP,
DecodeEventType.CALL_PATCH_GROUP,
DecodeEventType.CALL_ALERT,
DecodeEventType.CALL_DETECT,
DecodeEventType.CALL_DO_NOT_MONITOR,
DecodeEventType.CALL_END,
DecodeEventType.CALL_INTERCONNECT,
DecodeEventType.CALL_UNIQUE_ID,
DecodeEventType.CALL_UNIT_TO_UNIT,
DecodeEventType.CALL_NO_TUNER,
DecodeEventType.CALL_TIMEOUT
));
super("Voice Calls", DecodeEventType.VOICE_CALLS);
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -20,24 +20,17 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Event filter for network commands.
*/
public class DecodedCommandEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedCommandEventFilter()
{
super("Commands", Arrays.asList(
DecodeEventType.ANNOUNCEMENT,
DecodeEventType.STATION_ID,
DecodeEventType.ACKNOWLEDGE,
DecodeEventType.PAGE,
DecodeEventType.QUERY,
DecodeEventType.RADIO_CHECK,
DecodeEventType.STATUS,
DecodeEventType.COMMAND,
DecodeEventType.EMERGENCY,
DecodeEventType.NOTIFICATION,
DecodeEventType.FUNCTION
));
super("Commands", DecodeEventType.COMMANDS);
}
}

View File

@@ -1,23 +1,36 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Event filter for data call event types
*/
public class DecodedDataEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedDataEventFilter()
{
super("Data Calls", Arrays.asList(
DecodeEventType.DATA_CALL,
DecodeEventType.DATA_CALL_ENCRYPTED,
DecodeEventType.DATA_PACKET,
DecodeEventType.GPS,
DecodeEventType.IP_PACKET,
DecodeEventType.UDP_PACKET,
DecodeEventType.SDM,
DecodeEventType.ID_ANI,
DecodeEventType.ID_UNIQUE
));
super("Data Calls", DecodeEventType.DATA_CALLS);
}
}

View File

@@ -1,22 +1,36 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Event filter for registration related events
*/
public class DecodedRegistrationEventFilter extends EventFilter
{
/**
* Constructor
*/
public DecodedRegistrationEventFilter()
{
super("Registrations", Arrays.asList(
DecodeEventType.AFFILIATE,
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE,
DecodeEventType.REGISTER,
DecodeEventType.REGISTER_ESN,
DecodeEventType.DEREGISTER,
DecodeEventType.REQUEST,
DecodeEventType.RESPONSE,
DecodeEventType.RESPONSE_PACKET
));
super("Registrations", DecodeEventType.REGISTRATION);
}
}

View File

@@ -1,15 +1,33 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideSplitButton;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class EventClearButton extends JideSplitButton
{
private static final long serialVersionUID = 1L;
@@ -21,37 +39,29 @@ public class EventClearButton extends JideSplitButton
JPanel historyPanel = new JPanel();
historyPanel.add(new JLabel("History Entries:"));
historyPanel.add(new JLabel("History Size:"));
final JSlider historySlider = initializeHistorySlider();
JLabel valueLabel = new JLabel(String.valueOf(maxHistoryCount));
historySlider.setValue(maxHistoryCount);
historySlider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
historySlider.addChangeListener(arg0 -> {
if (mEventClearHandler != null)
{
if (mEventClearHandler != null)
{
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
}
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
}
valueLabel.setText(String.valueOf(historySlider.getValue()));
});
historyPanel.add(historySlider);
historyPanel.add(valueLabel);
add(historyPanel);
/* This handles the click action on the main button. Clear messages */
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
addActionListener(e -> {
if (mEventClearHandler != null)
{
if (mEventClearHandler != null)
{
mEventClearHandler.onClearHistoryClicked();
}
mEventClearHandler.onClearHistoryClicked();
}
});
}

View File

@@ -1,6 +1,37 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
public interface EventClearHandler {
/**
* Handler interface for changes to the event history size or content.
*/
public interface EventClearHandler
{
/**
* Invoked when the retained history size limit is changed
* @param newHistoryLimit new size
*/
void onHistoryLimitChanged(int newHistoryLimit);
/**
* Invoked when the history is cleared
*/
void onClearHistoryClicked();
}

View File

@@ -1,56 +1,74 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.function.Function;
/**
* This is used as a base for {@link IDecodeEvent} types.
* This will take a list of {@link IDecodeEvent}s and generate filters for the list.
* This will default canProcess to TRUE. Override if other value or evaluation is needed.
*
*/
public class EventFilter extends Filter<IDecodeEvent>
public class EventFilter extends Filter<IDecodeEvent,DecodeEventType>
{
private Map<DecodeEventType, FilterElement<DecodeEventType>> mElements = new EnumMap<>(DecodeEventType.class);
private EventKeyExtractor mKeyExtractor = new EventKeyExtractor();
public EventFilter(String name, List<DecodeEventType> eventTypes)
/**
* Constructs an instance
* @param name of this filter
* @param decodeEventTypes to filter against
*/
public EventFilter(String name, Collection<DecodeEventType> decodeEventTypes)
{
super(name);
for (DecodeEventType eventType : eventTypes) {
mElements.put(eventType, new FilterElement<>(eventType));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
for (DecodeEventType decodeEventType : decodeEventTypes)
{
if (mElements.containsKey(iDecodeEvent.getEventType()))
{
return mElements.get(iDecodeEvent.getEventType()).isEnabled();
}
add(new FilterElement<>(decodeEventType));
}
return false;
}
/**
* Key extractor function
* @return function.
*/
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
public Function<IDecodeEvent, DecodeEventType> getKeyExtractor()
{
return true;
return mKeyExtractor;
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor function for decode events to extract the decode event type
*/
public static class EventKeyExtractor implements Function<IDecodeEvent,DecodeEventType>
{
return new ArrayList<>(mElements.values());
@Override
public DecodeEventType apply(IDecodeEvent iDecodeEvent)
{
return iDecodeEvent.getEventType();
}
}
}

View File

@@ -1,84 +1,103 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideButton;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import net.miginfocom.swing.MigLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
/**
* Event filter button that includes split button functionality to allow user to select filter items.
*
* @param <T> type of filter.
*/
public class EventFilterButton<T> extends JideButton
{
private static final long serialVersionUID = 1L;
/**
* Constructs an instance
* @param dialogTitle for the dialog/panel that appears
* @param filterSet to include in the editor panel.
*/
public EventFilterButton(String dialogTitle, FilterSet<T> filterSet)
{
this("Filter", dialogTitle, filterSet);
}
/**
* Constructs an instance
* @param buttonLabel to use on the button
* @param dialogTitle for the dialog/panel that appears
* @param filterSet to include in the editor panel.
*/
public EventFilterButton(String buttonLabel, String dialogTitle, FilterSet<T> filterSet)
{
super(buttonLabel);
addActionListener(
new EventFilterActionHandler(dialogTitle, filterSet)
);
addActionListener(new EventFilterActionHandler(dialogTitle, filterSet));
}
/**
* Action handler for the button
*/
public class EventFilterActionHandler implements ActionListener
{
private String title;
private FilterSet<T> filterSet;
private String mTitle;
private FilterSet<T> mFilterSet;
public EventFilterActionHandler( String title, FilterSet<T> filterSet)
/**
* Constructs an instance
* @param title for this panel
* @param filterSet to edit
*/
public EventFilterActionHandler(String title, FilterSet<T> filterSet)
{
this.title = title;
this.filterSet = filterSet;
mTitle = title;
mFilterSet = filterSet;
}
@Override
public void actionPerformed(ActionEvent e)
{
final JFrame editor = new JFrame();
editor.setTitle(title);
editor.setTitle(mTitle);
editor.setLocationRelativeTo(EventFilterButton.this);
editor.setSize(600, 400);
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
editor.setLayout(new MigLayout("", "[grow,fill]",
"[grow,fill][][]"));
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(filterSet);
editor.setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][][]"));
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(mFilterSet);
JScrollPane scroller = new JScrollPane(panel);
scroller.setViewportView(panel);
editor.add(scroller, "wrap");
editor.add(new JLabel("Right-click to select/deselect all nodes"), "wrap");
JButton close = new JButton("Close");
close.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
editor.dispose();
}
});
close.addActionListener(e1 -> editor.dispose());
editor.add(close);
EventQueue.invokeLater(new Runnable()
{
public void run()
{
editor.setVisible(true);
}
});
EventQueue.invokeLater(() -> editor.setVisible(true));
}
}
}

View File

@@ -1,8 +0,0 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
public interface EventFilterProvider<T> {
FilterSet<T> getFilterSet();
void setFilterSet(FilterSet<T> filterSet);
}

View File

@@ -1,17 +0,0 @@
package io.github.dsheirer.module.decode.event.filter.lastheard;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class LastHeardFilterSet extends FilterSet<IDecodeEvent>
{
public LastHeardFilterSet(AliasModel aliasModel, IconModel iconModel)
{
super("Last Heard To Channel");
addFilter(new LastHeardIconFilter("Icon", aliasModel, iconModel));
}
}

View File

@@ -1,77 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter.lastheard;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.module.HistoryModule;
import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventListener;
import io.github.dsheirer.sample.Listener;
/**
* Decode event history module. Maintains a history of decode events and constrains the total history size.
*/
public class LastHeardHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
{
/**
* Constructs an instance
*/
public LastHeardHistory(int historySize)
{
super(historySize);
}
/**
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
*/
@Override
public Listener<IDecodeEvent> getDecodeEventListener()
{
return this;
}
/**
* Process preload data
*/
@Subscribe
public void process(DecodeEventHistoryPreloadData preloadData)
{
for(IDecodeEvent decodeEvent: preloadData.getData())
{
receive(decodeEvent);
}
}
/**
* Processes a request for decode event history and posts the response back
* to the processing chain event bus so that any of the modules can receive that history.
*
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
* event history from a traffic channel conversion to the new rest channel.
*
* @param request for decode event history
*/
@Subscribe
public void process(LastHeardHistoryRequest request)
{
getInterModuleEventBus().post(new LastHeardHistoryResponse(this));
}
}

View File

@@ -1,35 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter.lastheard;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Request for decode event history from a processing chain for the specified channel.
*/
public class LastHeardHistoryRequest extends ModuleEventBusMessage
{
/**
* Constructs an instance
*/
public LastHeardHistoryRequest()
{
}
}

View File

@@ -1,97 +0,0 @@
package io.github.dsheirer.module.decode.event.filter.lastheard;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import javafx.collections.ObservableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class LastHeardIconFilter extends Filter<IDecodeEvent>
{
private AliasModel mAliasModel;
private Map<String, FilterElement<String>> mElements = new HashMap<>();
public LastHeardIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
{
super(name);
this.mAliasModel = aliasModel;
ObservableList<Icon> icons = iconModel.iconsProperty();
for (Icon icon : icons) {
mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
{
return matchAliasToFilter(iDecodeEvent)
.map(FilterElement::isEnabled)
.orElse(false);
}
return false;
}
public Optional<FilterElement<String>> matchAliasToFilter(IDecodeEvent iDecodeEvent)
{
IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
List<Identifier> identifiers = identifierCollection.getIdentifiers(Role.TO);
if (iDecodeEvent.getDuration() > 0 && identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if (aliasList != null)
{
for(Identifier identifier: identifiers)
{
if (identifier.getForm() == Form.TALKGROUP)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if (!aliases.isEmpty())
{
for (Alias alias : aliases) {
if (alias.getIconName() == null)
{
// Return any to match aliases with no icon defined.
return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
}
else
{
return Optional.of(mElements.get(alias.getIconName()));
}
}
}
}
}
}
}
return Optional.empty();
}
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
{
return true;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<>(mElements.values());
}
}

View File

@@ -1,292 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.event.filter.lastheard;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LastHeardModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(LastHeardModel.class);
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
public static final int COLUMN_FROM_ID = 3;
public static final int COLUMN_FROM_ALIAS = 4;
public static final int COLUMN_TO_ID = 5;
public static final int COLUMN_TO_ALIAS = 6;
public static final int COLUMN_CHANNEL = 7;
public static final int COLUMN_FREQUENCY = 8;
public static final int COLUMN_DETAILS = 9;
protected int mMaxMessages = 500;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mLastSeenFilterSet;
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public LastHeardModel(AliasModel aliasModel, IconModel iconModel)
{
mLastSeenFilterSet = new LastHeardFilterSet(aliasModel, iconModel);
MyEventBus.getGlobalEventBus().register(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_TIME);
}
}
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_FROM_ID);
fireTableCellUpdated(row, COLUMN_TO_ID);
}
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
{
it.remove();
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
* Adds, updates or deletes the event from the model. Producers can send
* the same call event multiple times to indicate that information in the
* event is updated. Producers can also mark the event as invalid and the
* event will be removed from the model.
*/
public void receive(final IDecodeEvent event)
{
if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
{
return;
}
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mLastSeenFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mLastSeenFilterSet = filterSet;
}
@Override
public int getRowCount()
{
return mEvents.size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName(int column)
{
return mHeaders[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
{
IDecodeEvent event = mEvents.get(rowIndex);
if(event != null)
{
switch(columnIndex)
{
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventDescription();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
if(channelDescriptor != null)
{
if(event.hasTimeslot())
{
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
}
else
{
return channelDescriptor.toString();
}
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch(columnIndex)
{
case COLUMN_DURATION:
case COLUMN_TIME:
return Long.class;
case COLUMN_DETAILS:
case COLUMN_EVENT:
return String.class;
case COLUMN_FREQUENCY:
case COLUMN_FROM_ALIAS:
case COLUMN_FROM_ID:
case COLUMN_TO_ALIAS:
case COLUMN_TO_ID:
return IdentifierCollection.class;
case COLUMN_CHANNEL:
return String.class;
}
return super.getColumnClass(columnIndex);
}
}

View File

@@ -1,482 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.event.filter.lastheard;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
import io.github.dsheirer.module.decode.event.DecodeEventModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class LastHeardPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(LastHeardPanel.class);
private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
private JTable mTable;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private LastHeardModel mLastHeardModel;
private DecodeEventHistory mCurrentEventHistory;
private JScrollPane mEmptyScroller;
private IconModel mIconModel;
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private LastSeenManagementPanel mLastSeenManagementPanel;
/**
* View for call event table
* @param iconModel to display alias icons in table rows
*/
public LastHeardPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
{
MyEventBus.getGlobalEventBus().register(this);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mIconModel = iconModel;
mAliasModel = aliasModel;
mLastHeardModel = new LastHeardModel(aliasModel, iconModel);
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mTable = new JTable(mLastHeardModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mLastSeenManagementPanel = new LastSeenManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
add(mLastSeenManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
}
}
private void updateCellRenderers()
{
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
.setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
.setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
.setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
}
@Override
public void receive(final ProcessingChain processingChain)
{
if(mCurrentEventHistory != null)
{
mCurrentEventHistory.removeListener(mLastHeardModel);
}
EventQueue.invokeLater(() -> {
if(processingChain != null)
{
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mLastHeardModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mLastHeardModel);
mLastSeenManagementPanel.enableButtons();
}
else
{
mCurrentEventHistory = null;
mLastHeardModel.clearAndSet(Collections.emptyList());
mLastSeenManagementPanel.disableButtons();
}
});
}
public class LastSeenManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public LastSeenManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Last Heard To Channel Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((LastHeardModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((LastHeardModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((LastHeardModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
public class IdentifierCellRenderer extends DefaultTableCellRenderer
{
protected Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public IdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof IdentifierCollection)
{
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
label.setText(format(identifiers));
}
else
{
label.setText(null);
}
return label;
}
/**
* Formats a list of identifiers as a comma separated list of values
* @param identifiers to format
* @return formatted list or null
*/
protected String format(List<Identifier> identifiers)
{
if(identifiers == null || identifiers.isEmpty())
{
return null;
}
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
if(sb.length() > 0)
{
sb.append(",");
}
if(identifier.getForm() == Form.TALKGROUP)
{
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
}
else
{
sb.append(identifier);
}
}
return sb.toString();
}
}
/**
* Cell renderer for identifier aliases
*/
public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
{
private Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public AliasedIdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color color = mTable.getForeground();
ImageIcon icon = null;
String text = null;
if(value instanceof IdentifierCollection)
{
IdentifierCollection identifierCollection = (IdentifierCollection)value;
List<Identifier> identifiers = identifierCollection.getIdentifiers(mRole);
if(identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if(!aliases.isEmpty())
{
if(sb.length() > 0)
{
sb.append(",");
}
sb.append(Joiner.on(", ").skipNulls().join(aliases));
color = aliases.get(0).getDisplayColor();
icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
}
}
text = sb.toString();
}
}
}
label.setText(text);
label.setForeground(color);
label.setIcon(icon);
return label;
}
}
public class TimestampCellRenderer extends DefaultTableCellRenderer
{
private SimpleDateFormat mTimestampFormatter;
public TimestampCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
updatePreferences();
}
public void updatePreferences()
{
mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof Long)
{
label.setText(mTimestampFormatter.format(new Date((long)value)));
}
else
{
label.setText(null);
}
return label;
}
}
public class DurationCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
public DurationCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof Long)
{
long duration = (long)value;
if(duration > 0)
{
formatted = mDecimalFormat.format((double)duration / 1e3d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Frequency value cell renderer
*/
public class FrequencyCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
public FrequencyCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof IChannelDescriptor)
{
IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
long frequency = channelDescriptor.getDownlinkFrequency();
if(frequency > 0)
{
formatted = mFrequencyFormatter.format(frequency / 1e6d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Channel descriptor value cell renderer
*/
public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
{
public ChannelDescriptorCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
}
}

View File

@@ -1,17 +0,0 @@
package io.github.dsheirer.module.decode.event.filter.lastseen;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class LastSeenFilterSet extends FilterSet<IDecodeEvent>
{
public LastSeenFilterSet(AliasModel aliasModel, IconModel iconModel)
{
super("Last Seen From Radio");
addFilter(new LastSeenIconFilter("Icon", aliasModel, iconModel));
}
}

View File

@@ -1,77 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter.lastseen;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.module.HistoryModule;
import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventListener;
import io.github.dsheirer.sample.Listener;
/**
* Decode event history module. Maintains a history of decode events and constrains the total history size.
*/
public class LastSeenHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
{
/**
* Constructs an instance
*/
public LastSeenHistory(int historySize)
{
super(historySize);
}
/**
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
*/
@Override
public Listener<IDecodeEvent> getDecodeEventListener()
{
return this;
}
/**
* Process preload data
*/
@Subscribe
public void process(DecodeEventHistoryPreloadData preloadData)
{
for(IDecodeEvent decodeEvent: preloadData.getData())
{
receive(decodeEvent);
}
}
/**
* Processes a request for decode event history and posts the response back
* to the processing chain event bus so that any of the modules can receive that history.
*
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
* event history from a traffic channel conversion to the new rest channel.
*
* @param request for decode event history
*/
@Subscribe
public void process(LastSeenHistoryRequest request)
{
getInterModuleEventBus().post(new LastSeenHistoryResponse(this));
}
}

View File

@@ -1,47 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event.filter.lastseen;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Response to decode event history request from a processing chain for the specified channel.
*/
public class LastSeenHistoryResponse extends ModuleEventBusMessage
{
private LastSeenHistory mLastSeenHistory;
/**
* Constructs an instance
* @param history that has a decode event history
*/
public LastSeenHistoryResponse(LastSeenHistory history)
{
mLastSeenHistory = history;
}
/**
* Decode event history module
*/
public LastSeenHistory getDecodeEventHistory()
{
return mLastSeenHistory;
}
}

View File

@@ -1,96 +0,0 @@
package io.github.dsheirer.module.decode.event.filter.lastseen;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import javafx.collections.ObservableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class LastSeenIconFilter extends Filter<IDecodeEvent>
{
private AliasModel mAliasModel;
private Map<String, FilterElement<String>> mElements = new HashMap<>();
public LastSeenIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
{
super(name);
this.mAliasModel = aliasModel;
ObservableList<Icon> icons = iconModel.iconsProperty();
for (Icon icon : icons) {
mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
{
return matchAliasToFilter(iDecodeEvent)
.map(FilterElement::isEnabled)
.orElse(false);
}
return false;
}
public Optional<FilterElement<String>> matchAliasToFilter(IDecodeEvent iDecodeEvent)
{
IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
List<Identifier> identifiers = identifierCollection.getIdentifiers();
if (identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if (aliasList != null)
{
for(Identifier identifier: identifiers)
{
if (identifier.getForm() == Form.RADIO)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if (!aliases.isEmpty())
{
for (Alias alias : aliases) {
if (alias.getIconName() == null)
{
// Return any to match aliases with no icon defined.
return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
}
else
{
return Optional.of(mElements.get(alias.getIconName()));
}
}
}
}
}
}
}
return Optional.empty();
}
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
{
return true;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<>(mElements.values());
}
}

View File

@@ -1,289 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.event.filter.lastseen;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LastSeenModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
public static final int COLUMN_FROM_ID = 3;
public static final int COLUMN_FROM_ALIAS = 4;
public static final int COLUMN_TO_ID = 5;
public static final int COLUMN_TO_ALIAS = 6;
public static final int COLUMN_CHANNEL = 7;
public static final int COLUMN_FREQUENCY = 8;
public static final int COLUMN_DETAILS = 9;
protected int mMaxMessages = 500;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mLastSeenFilterSet;
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public LastSeenModel(AliasModel aliasModel, IconModel iconModel)
{
mLastSeenFilterSet = new LastSeenFilterSet(aliasModel, iconModel);
MyEventBus.getGlobalEventBus().register(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_TIME);
}
}
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_FROM_ID);
fireTableCellUpdated(row, COLUMN_TO_ID);
}
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
{
it.remove();
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
* Adds, updates or deletes the event from the model. Producers can send
* the same call event multiple times to indicate that information in the
* event is updated. Producers can also mark the event as invalid and the
* event will be removed from the model.
*/
public void receive(final IDecodeEvent event)
{
if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
{
return;
}
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mLastSeenFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mLastSeenFilterSet = filterSet;
}
@Override
public int getRowCount()
{
return mEvents.size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName(int column)
{
return mHeaders[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
{
IDecodeEvent event = mEvents.get(rowIndex);
if(event != null)
{
switch(columnIndex)
{
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventDescription();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
if(channelDescriptor != null)
{
if(event.hasTimeslot())
{
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
}
else
{
return channelDescriptor.toString();
}
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch(columnIndex)
{
case COLUMN_DURATION:
case COLUMN_TIME:
return Long.class;
case COLUMN_DETAILS:
case COLUMN_EVENT:
return String.class;
case COLUMN_FREQUENCY:
case COLUMN_FROM_ALIAS:
case COLUMN_FROM_ID:
case COLUMN_TO_ALIAS:
case COLUMN_TO_ID:
return IdentifierCollection.class;
case COLUMN_CHANNEL:
return String.class;
}
return super.getColumnClass(columnIndex);
}
}

View File

@@ -1,482 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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.event.filter.lastseen;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
import io.github.dsheirer.module.decode.event.DecodeEventModel;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class LastSeenPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(LastSeenPanel.class);
private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
private JTable mTable;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private LastSeenModel mLastSeenModel;
private DecodeEventHistory mCurrentEventHistory;
private JScrollPane mEmptyScroller;
private IconModel mIconModel;
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private LastSeenManagementPanel mLastSeenManagementPanel;
/**
* View for call event table
* @param iconModel to display alias icons in table rows
*/
public LastSeenPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
{
MyEventBus.getGlobalEventBus().register(this);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mIconModel = iconModel;
mAliasModel = aliasModel;
mLastSeenModel = new LastSeenModel(aliasModel, iconModel);
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mTable = new JTable(mLastSeenModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mLastSeenManagementPanel = new LastSeenManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
add(mLastSeenManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
*/
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
}
}
private void updateCellRenderers()
{
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
.setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
.setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
.setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
}
@Override
public void receive(final ProcessingChain processingChain)
{
if(mCurrentEventHistory != null)
{
mCurrentEventHistory.removeListener(mLastSeenModel);
}
EventQueue.invokeLater(() -> {
if(processingChain != null)
{
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mLastSeenModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mLastSeenModel);
mLastSeenManagementPanel.enableButtons();
}
else
{
mCurrentEventHistory = null;
mLastSeenModel.clearAndSet(Collections.emptyList());
mLastSeenManagementPanel.disableButtons();
}
});
}
public class LastSeenManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public LastSeenManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Last Seen From Radio Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((LastSeenModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((LastSeenModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((LastSeenModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
public class IdentifierCellRenderer extends DefaultTableCellRenderer
{
protected Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public IdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof IdentifierCollection)
{
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
label.setText(format(identifiers));
}
else
{
label.setText(null);
}
return label;
}
/**
* Formats a list of identifiers as a comma separated list of values
* @param identifiers to format
* @return formatted list or null
*/
protected String format(List<Identifier> identifiers)
{
if(identifiers == null || identifiers.isEmpty())
{
return null;
}
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
if(sb.length() > 0)
{
sb.append(",");
}
if(identifier.getForm() == Form.TALKGROUP)
{
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
}
else
{
sb.append(identifier);
}
}
return sb.toString();
}
}
/**
* Cell renderer for identifier aliases
*/
public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
{
private Role mRole;
/**
* Constructs an instance of the cell renderer.
*
* @param role of the identifier
*/
public AliasedIdentifierCellRenderer(Role role)
{
mRole = role;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color color = mTable.getForeground();
ImageIcon icon = null;
String text = null;
if(value instanceof IdentifierCollection)
{
IdentifierCollection identifierCollection = (IdentifierCollection)value;
List<Identifier> identifiers = identifierCollection.getIdentifiers(mRole);
if(identifiers != null && !identifiers.isEmpty())
{
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
StringBuilder sb = new StringBuilder();
for(Identifier identifier: identifiers)
{
List<Alias> aliases = aliasList.getAliases(identifier);
if(!aliases.isEmpty())
{
if(sb.length() > 0)
{
sb.append(",");
}
sb.append(Joiner.on(", ").skipNulls().join(aliases));
color = aliases.get(0).getDisplayColor();
icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
}
}
text = sb.toString();
}
}
}
label.setText(text);
label.setForeground(color);
label.setIcon(icon);
return label;
}
}
public class TimestampCellRenderer extends DefaultTableCellRenderer
{
private SimpleDateFormat mTimestampFormatter;
public TimestampCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
updatePreferences();
}
public void updatePreferences()
{
mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof Long)
{
label.setText(mTimestampFormatter.format(new Date((long)value)));
}
else
{
label.setText(null);
}
return label;
}
}
public class DurationCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
public DurationCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof Long)
{
long duration = (long)value;
if(duration > 0)
{
formatted = mDecimalFormat.format((double)duration / 1e3d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Frequency value cell renderer
*/
public class FrequencyCellRenderer extends DefaultTableCellRenderer
{
private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
public FrequencyCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
String formatted = null;
if(value instanceof IChannelDescriptor)
{
IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
long frequency = channelDescriptor.getDownlinkFrequency();
if(frequency > 0)
{
formatted = mFrequencyFormatter.format(frequency / 1e6d);
}
}
label.setText(formatted);
return label;
}
}
/**
* Channel descriptor value cell renderer
*/
public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
{
public ChannelDescriptorCellRenderer()
{
setHorizontalAlignment(JLabel.CENTER);
}
}
}

View File

@@ -2,24 +2,19 @@
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.fleetsync2;
@@ -125,10 +120,8 @@ public class Fleetsync2DecoderState extends DecoderState
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA));
break;
case GPS:
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp())
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, fleetsync.getTimestamp())
.channel(getCurrentChannel())
.eventType(DecodeEventType.GPS)
.eventDescription(DecodeEventType.GPS.toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.protocol(Protocol.FLEETSYNC)
@@ -143,11 +136,9 @@ public class Fleetsync2DecoderState extends DecoderState
}
private DecodeEvent getDecodeEvent(Fleetsync2Message fleetsync, DecodeEventType eventType) {
return DecodeEvent.builder(fleetsync.getTimestamp())
return DecodeEvent.builder(eventType, fleetsync.getTimestamp())
.channel(getCurrentChannel())
.eventType(eventType)
.eventDescription(fleetsync.getMessageType().toString())
.details(fleetsync.toString())
.details(fleetsync.getMessageType() + " " + fleetsync)
.identifiers(getIdentifierCollection().copyOf())
.protocol(Protocol.FLEETSYNC)
.build();

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.fleetsync2;
@@ -24,52 +23,63 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message;
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
import java.util.function.Function;
import java.util.*;
public class FleetsyncMessageFilter extends Filter<IMessage>
/**
* Filter for Fleetsync messages.
*/
public class FleetsyncMessageFilter extends Filter<IMessage,FleetsyncMessageType>
{
private Map<FleetsyncMessageType,FilterElement<FleetsyncMessageType>> mElements = new EnumMap<>(FleetsyncMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public FleetsyncMessageFilter()
{
super("Fleetsync Message Filter");
super("Fleetsync Messages");
for(FleetsyncMessageType type : FleetsyncMessageType.values())
{
if(type != FleetsyncMessageType.UNKNOWN)
{
mElements.put(type, new FilterElement<FleetsyncMessageType>(type));
}
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
Fleetsync2Message fleet = (Fleetsync2Message)message;
if(mElements.containsKey(fleet.getMessageType()))
{
return mElements.get(fleet.getMessageType()).isEnabled();
}
}
return false;
}
/**
* Limits processing to only fleetsync messages.
* @param message to test
* @return true if message type is correct.
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof Fleetsync2Message;
return message instanceof Fleetsync2Message && super.canProcess(message);
}
/**
* Key extractor for fleetsync messages
* @return key extractor
*/
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, FleetsyncMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor for Fleetsync 2 Messages.
*/
private class KeyExtractor implements Function<IMessage, FleetsyncMessageType>
{
@Override
public FleetsyncMessageType apply(IMessage message)
{
if(message instanceof Fleetsync2Message f2m)
{
return f2m.getMessageType();
}
return null;
}
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.lj1200;
@@ -31,14 +28,14 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.dsheirer.protocol.Protocol;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LJ1200DecoderState extends DecoderState
{
@@ -73,12 +70,11 @@ public class LJ1200DecoderState extends DecoderState
ic.remove(IdentifierClass.USER);
ic.update(lj.getIdentifiers());
DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis())
DecodeEvent event = DecodeEvent.builder(DecodeEventType.DATA_PACKET, System.currentTimeMillis())
.protocol(Protocol.LOJACK)
.identifiers(ic)
.eventType(DecodeEventType.DATA_PACKET)
.channel(getCurrentChannel())
.details("LOJACK")
.eventDescription(lj.toString())
.details("LOJACK " + lj)
.build();
broadcast(event);
@@ -93,12 +89,11 @@ public class LJ1200DecoderState extends DecoderState
ic.remove(IdentifierClass.USER);
ic.update(transponder.getIdentifiers());
DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis())
DecodeEvent transponderEvent = DecodeEvent.builder(DecodeEventType.GPS, System.currentTimeMillis())
.protocol(Protocol.LOJACK)
.identifiers(ic)
.eventType(DecodeEventType.GPS)
.channel(getCurrentChannel())
.details("LOJACK TRANSPONDER")
.eventDescription(transponder.toString())
.details("LOJACK TRANSPONDER " + transponder)
.build();
broadcast(transponderEvent);
@@ -117,7 +112,7 @@ public class LJ1200DecoderState extends DecoderState
{
sb.append("Transponder Addresses:\n");
List<String> addresses = new ArrayList<String>(mAddresses);
List<String> addresses = new ArrayList<>(mAddresses);
Collections.sort(addresses);

View File

@@ -1,52 +1,66 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 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.
* 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/>
******************************************************************************/
* 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.lj1200;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class LJ1200MessageFilter extends Filter<IMessage>
/**
* Filter for LJ1200 messages
*/
public class LJ1200MessageFilter extends Filter<IMessage,String>
{
private static final String LJ1200_KEY = "LJ-1200";
private final LJ1200KeyExtractor mKeyExtractor = new LJ1200KeyExtractor();
/**
* Constructor
*/
public LJ1200MessageFilter()
{
super("LJ-1200 Message Filter");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
super("LJ-1200 Messages");
add(new FilterElement<>(LJ1200_KEY));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage;
return (message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, String> getKeyExtractor()
{
return Collections.EMPTY_LIST;
return mKeyExtractor;
}
/**
* Key extractor
*/
public class LJ1200KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
return LJ1200_KEY;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,23 +14,27 @@
*
* 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.ltrnet;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* LTRNet decode event
*/
public class LTRNetDecodeEvent extends DecodeEvent
{
/**
* Constructs an LTR NET decode event
* @param start
*/
public LTRNetDecodeEvent(long start)
public LTRNetDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.LTR_NET);
}
@@ -40,9 +43,9 @@ public class LTRNetDecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.LTR_NET);
return decodeEventBuilder;
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.ltrnet;
@@ -61,9 +58,6 @@ import io.github.dsheirer.module.decode.ltrnet.message.osw.SystemIdle;
import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh;
import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow;
import io.github.dsheirer.protocol.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -72,6 +66,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LTRNetDecoderState extends DecoderState
{
@@ -167,9 +163,24 @@ public class LTRNetDecoderState extends DecoderState
if(mCurrentCallTalkgroup == null || !mCurrentCallTalkgroup.equals(talkgroup))
{
DecodeEventType decodeEventType = null;
if(talkgroup.getTalkgroup() == 253)
{
decodeEventType = DecodeEventType.REGISTER;
}
else if(talkgroup.getTalkgroup() == 254)
{
decodeEventType = DecodeEventType.STATION_ID;
}
else
{
decodeEventType = DecodeEventType.CALL;
}
getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(talkgroup);
mCurrentCallEvent = LTRNetDecodeEvent.builder(timestamp)
mCurrentCallEvent = LTRNetDecodeEvent.builder(decodeEventType, timestamp)
.channel(getCurrentChannel())
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -181,20 +192,14 @@ public class LTRNetDecoderState extends DecoderState
if(talkgroup.getTalkgroup() == 253)
{
mCurrentCallEvent.setEventType(DecodeEventType.REGISTER);
mCurrentCallEvent.setEventDescription("Register");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else if(talkgroup.getTalkgroup() == 254)
{
mCurrentCallEvent.setEventType(DecodeEventType.STATION_ID);
mCurrentCallEvent.setEventDescription("FCC CWID");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else
{
mCurrentCallEvent.setEventType(DecodeEventType.CALL);
mCurrentCallEvent.setEventDescription("Call");
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
@@ -292,77 +297,59 @@ public class LTRNetDecoderState extends DecoderState
switch(((LtrNetMessage)message).getLtrNetMessageType())
{
case ISW_CALL_END:
if(message instanceof IswCallEnd)
if(message instanceof IswCallEnd callEnd)
{
IswCallEnd callEnd = (IswCallEnd)message;
mTalkgroups.add(callEnd.getTalkgroup());
processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp());
}
break;
case ISW_CALL_START:
if(message instanceof IswCallStart)
if(message instanceof IswCallStart callStart)
{
IswCallStart callStart = (IswCallStart)message;
mTalkgroups.add(callStart.getTalkgroup());
processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(),
MessageDirection.ISW);
}
break;
case ISW_REGISTRATION_REQUEST_ESN_HIGH:
if(message instanceof RegistrationRequestEsnHigh)
if(message instanceof RegistrationRequestEsnHigh registrationRequestEsn)
{
RegistrationRequestEsnHigh registrationRequestEsn = (RegistrationRequestEsnHigh)message;
if(registrationRequestEsn.isCompleteEsn())
{
getIdentifierCollection().update(registrationRequestEsn.getESN());
mESNIdentifiers.add(registrationRequestEsn.getESN());
}
broadcast(getDecodeEvent(
message, DecodeEventType.REQUEST,
"Registration Request", registrationRequestEsn.toString()));
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
break;
case ISW_REGISTRATION_REQUEST_ESN_LOW:
if(message instanceof RegistrationRequestEsnLow)
if(message instanceof RegistrationRequestEsnLow registrationRequestEsn)
{
RegistrationRequestEsnLow registrationRequestEsn = (RegistrationRequestEsnLow)message;
if(registrationRequestEsn.isCompleteEsn())
{
getIdentifierCollection().update(registrationRequestEsn.getESN());
mESNIdentifiers.add(registrationRequestEsn.getESN());
}
broadcast(getDecodeEvent(
message, DecodeEventType.REQUEST,
"Registration Request", registrationRequestEsn.toString()));
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
break;
case ISW_REQUEST_ACCESS:
if(message instanceof RequestAccess)
if(message instanceof RequestAccess requestAccess)
{
RequestAccess requestAccess = (RequestAccess)message;
getIdentifierCollection().update(requestAccess.getTalkgroup());
mTalkgroups.add(requestAccess.getTalkgroup());
broadcast(getDecodeEvent(
message, DecodeEventType.REQUEST,
"Access Request", requestAccess.toString()));
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "ACCESS: " + requestAccess));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
break;
case ISW_UNIQUE_ID:
if(message instanceof IswUniqueId)
if(message instanceof IswUniqueId iswUniqueId)
{
IswUniqueId iswUniqueId = (IswUniqueId)message;
getIdentifierCollection().update(iswUniqueId.getUniqueID());
mLtrNetRadioIdentifiers.add(iswUniqueId.getUniqueID());
updateCurrentCallIdentifiers();
@@ -373,99 +360,90 @@ public class LTRNetDecoderState extends DecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_CALL_END:
if(message instanceof OswCallEnd)
if(message instanceof OswCallEnd callEnd)
{
OswCallEnd callEnd = (OswCallEnd)message;
mTalkgroups.add(callEnd.getTalkgroup());
processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp());
}
break;
case OSW_CALL_START:
if(message instanceof OswCallStart)
if(message instanceof OswCallStart callStart)
{
OswCallStart callStart = (OswCallStart)message;
mTalkgroups.add(callStart.getTalkgroup());
processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(),
MessageDirection.OSW);
}
break;
case OSW_CHANNEL_MAP_HIGH:
if(message instanceof ChannelMapHigh)
if(message instanceof ChannelMapHigh channelMapHigh)
{
mChannelMapHigh = (ChannelMapHigh)message;
mChannelMapHigh = channelMapHigh;
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_CHANNEL_MAP_LOW:
if(message instanceof ChannelMapLow)
if(message instanceof ChannelMapLow channelMapLow)
{
mChannelMapLow = (ChannelMapLow)message;
mChannelMapLow = channelMapLow;
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_SYSTEM_IDLE:
if(message instanceof SystemIdle)
if(message instanceof SystemIdle systemIdle)
{
setCurrentChannelNumber(((SystemIdle)message).getChannel());
setCurrentChannelNumber(systemIdle.getChannel());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_NEIGHBOR_ID:
if(message instanceof NeighborId)
if(message instanceof NeighborId neighborId)
{
NeighborId neighborId = (NeighborId)message;
mNeighborMap.put(neighborId.getNeighborRank(), neighborId);
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_RECEIVE_FREQUENCY_HIGH:
if(message instanceof ReceiveFrequencyHigh)
if(message instanceof ReceiveFrequencyHigh receiveFrequency)
{
ReceiveFrequencyHigh receiveFrequency = (ReceiveFrequencyHigh)message;
updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_RECEIVE_FREQUENCY_LOW:
if(message instanceof ReceiveFrequencyLow)
if(message instanceof ReceiveFrequencyLow receiveFrequency)
{
ReceiveFrequencyLow receiveFrequency = (ReceiveFrequencyLow)message;
updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_REGISTRATION_ACCEPT:
if(message instanceof RegistrationAccept)
if(message instanceof RegistrationAccept registrationAccept)
{
RegistrationAccept registrationAccept = (RegistrationAccept)message;
mLtrNetRadioIdentifiers.add(registrationAccept.getUniqueID());
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE,
"Registration Accept", registrationAccept.toString()));
broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE, "REGISTRATION ACCEPT: " + registrationAccept));
}
break;
case OSW_SITE_ID:
if(message instanceof SiteId)
if(message instanceof SiteId siteId)
{
mCurrentSite = (SiteId)message;
mCurrentSite = siteId;
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_TRANSMIT_FREQUENCY_HIGH:
if(message instanceof TransmitFrequencyHigh)
if(message instanceof TransmitFrequencyHigh transmitFrequency)
{
TransmitFrequencyHigh transmitFrequency = (TransmitFrequencyHigh)message;
updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_TRANSMIT_FREQUENCY_LOW:
if(message instanceof TransmitFrequencyLow)
if(message instanceof TransmitFrequencyLow transmitFrequency)
{
TransmitFrequencyLow transmitFrequency = (TransmitFrequencyLow)message;
updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
@@ -477,24 +455,22 @@ public class LTRNetDecoderState extends DecoderState
}
}
private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String description, String details) {
return LTRNetDecodeEvent.builder(message.getTimestamp())
.eventType(decodeEventType)
.eventDescription(description)
private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String details)
{
return LTRNetDecodeEvent.builder(decodeEventType, message.getTimestamp())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.build();
}
private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType) {
private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType)
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
return LTRNetDecodeEvent.builder(timestamp)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
return LTRNetDecodeEvent.builder(decodeEventType, timestamp)
.channel(mChannelMap.get(channel))
.identifiers(ic)
.build();

View File

@@ -1,52 +1,85 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ltrnet;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage;
import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType;
import java.util.function.Function;
import java.util.*;
public class LTRNetMessageFilter extends Filter<IMessage>
/**
* Filter for LTR-Net messages
*/
public class LTRNetMessageFilter extends Filter<IMessage,LtrNetMessageType>
{
private Map<LtrNetMessageType, FilterElement<LtrNetMessageType>> mElements = new EnumMap<>(LtrNetMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public LTRNetMessageFilter()
{
super("LTR-Net Message Filter");
super("LTR-Net Messages");
for(LtrNetMessageType type: LtrNetMessageType.values())
{
mElements.put(type, new FilterElement<>(type));
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
LtrNetMessage ltr = (LtrNetMessage) message;
if(mElements.containsKey(ltr.getLtrNetMessageType()))
{
return mElements.get(ltr.getLtrNetMessageType()).isEnabled();
}
}
return false;
}
/**
* Tests for instance of LTR-Net message
* @param message to test
* @return true if correct type.
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LtrNetMessage;
return message instanceof LtrNetMessage && super.canProcess(message);
}
/**
* Key extractor for LTR-Net messages
* @return extractor
*/
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, LtrNetMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LtrNetMessageType>
{
@Override
public LtrNetMessageType apply(IMessage message)
{
if(message instanceof LtrNetMessage ltr)
{
return ltr.getLtrNetMessageType();
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,23 +14,27 @@
*
* 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.ltrstandard;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* LTR decode event
*/
public class LTRStandardDecodeEvent extends DecodeEvent
{
/**
* Constructs an LTR Standard decode event
* @param start
*/
public LTRStandardDecodeEvent(long start)
public LTRStandardDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.LTR);
}
@@ -40,9 +43,9 @@ public class LTRStandardDecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.LTR);
return decodeEventBuilder;
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.ltrstandard;
@@ -42,9 +39,6 @@ import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd;
import io.github.dsheirer.module.decode.ltrstandard.message.Idle;
import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage;
import io.github.dsheirer.protocol.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -52,6 +46,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LTRStandardDecoderState extends DecoderState
{
@@ -82,10 +78,8 @@ public class LTRStandardDecoderState extends DecoderState
switch(((LTRMessage)message).getMessageType())
{
case CALL:
if(message instanceof Call)
if(message instanceof Call start)
{
Call start = (Call)message;
int channel = start.getChannel();
setChannelNumber(channel);
mLCNTracker.processFreeChannel(start.getFree());
@@ -98,11 +92,9 @@ public class LTRStandardDecoderState extends DecoderState
mCurrentTalkgroup = start.getTalkgroup();
getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(start.getTalkgroup());
mCurrentCallEvent = LTRStandardDecodeEvent.builder(start.getTimestamp())
mCurrentCallEvent = LTRStandardDecodeEvent.builder(DecodeEventType.CALL, start.getTimestamp())
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.eventType(DecodeEventType.CALL)
.eventDescription("Call")
.build();
}
else
@@ -115,10 +107,8 @@ public class LTRStandardDecoderState extends DecoderState
}
break;
case CALL_END:
if(message instanceof CallEnd)
if(message instanceof CallEnd end)
{
CallEnd end = (CallEnd)message;
mCurrentTalkgroup = null;
//Home channel is 31 for call end -- use the free channel as the call end channel
@@ -136,10 +126,10 @@ public class LTRStandardDecoderState extends DecoderState
}
break;
case IDLE:
if(message instanceof Idle)
if(message instanceof Idle idle)
{
mCurrentTalkgroup = null;
mLCNTracker.processCallChannel(((Idle)message).getChannel());
mLCNTracker.processCallChannel(idle.getChannel());
}
break;
default:

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.ltrstandard;
@@ -24,47 +23,53 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage;
import java.util.function.Function;
import java.util.*;
public class LTRStandardMessageFilter extends Filter<IMessage>
/**
* Filter for LTR standard messages.
*/
public class LTRStandardMessageFilter extends Filter<IMessage,LtrStandardMessageType>
{
private Map<LtrStandardMessageType,FilterElement<LtrStandardMessageType>> mElements = new EnumMap<>(LtrStandardMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public LTRStandardMessageFilter()
{
super("LTR Message Filter");
super("LTR Messages");
mElements.put(LtrStandardMessageType.CALL, new FilterElement<>(LtrStandardMessageType.CALL));
mElements.put(LtrStandardMessageType.CALL_END, new FilterElement<>(LtrStandardMessageType.CALL_END));
mElements.put(LtrStandardMessageType.IDLE, new FilterElement<>(LtrStandardMessageType.IDLE));
mElements.put(LtrStandardMessageType.UNKNOWN, new FilterElement<>(LtrStandardMessageType.UNKNOWN));
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
for(LtrStandardMessageType type: LtrStandardMessageType.values())
{
LTRMessage ltr = (LTRMessage)message;
if(mElements.containsKey(ltr.getMessageType()))
{
return mElements.get(ltr.getMessageType()).isEnabled();
}
add(new FilterElement<>(type));
}
return false;
}
public boolean canProcess(IMessage message)
{
return message instanceof LTRMessage;
return message instanceof LTRMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, LtrStandardMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LtrStandardMessageType>
{
@Override
public LtrStandardMessageType apply(IMessage message)
{
if(message instanceof LTRMessage ltr)
{
return ltr.getMessageType();
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,23 +14,28 @@
*
* 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.mdc1200;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* MDC-1200 decode event
*/
public class MDCDecodeEvent extends DecodeEvent
{
/**
* Constucts a P25 decode event
* @param start
* Constucts an MDC1200 decode event
* @param decodeEventType for the event
* @param start for the event
*/
public MDCDecodeEvent(long start)
public MDCDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.MDC1200);
}
@@ -40,9 +44,9 @@ public class MDCDecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.MDC1200);
return decodeEventBuilder;
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.mdc1200;
@@ -31,7 +28,6 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
@@ -114,9 +110,7 @@ public class MDCDecoderState extends DecoderState
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
broadcast(MDCDecodeEvent.builder(mdc.getTimestamp())
.eventType(getDecodeEventType(type))
.eventDescription(type.getLabel())
broadcast(MDCDecodeEvent.builder(getDecodeEventType(type), mdc.getTimestamp())
.details(mdc.toString())
.identifiers(ic)
.build());

View File

@@ -1,55 +1,75 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.mdc1200;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.Message;
import io.github.dsheirer.module.decode.mpt1327.MPT1327Message;
import java.util.function.Function;
import java.util.*;
public class MDCMessageFilter extends Filter<IMessage>
/**
* Message filter for MDC-1200 messages
*/
public class MDCMessageFilter extends Filter<IMessage,MDCMessageType>
{
private Map<MDCMessageType, FilterElement<MDCMessageType>> mElements = new EnumMap<>(MDCMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public MDCMessageFilter()
{
super("MDC-1200 Message Filter");
super("MDC-1200 Messages");
for(MDCMessageType type : MDCMessageType.values())
{
if(type != MDCMessageType.UNKNOWN)
{
mElements.put(type, new FilterElement<MDCMessageType>(type));
}
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
MDCMessage mdc = (MDCMessage) message;
if(mElements.containsKey(mdc.getMessageType()))
{
return mElements.get(mdc.getMessageType()).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof MDCMessage;
return message instanceof MDCMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, MDCMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,MDCMessageType>
{
@Override
public MDCMessageType apply(IMessage message)
{
if(message instanceof MDCMessage mdc)
{
return mdc.getMessageType();
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,21 +14,23 @@
*
* 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.mpt1327;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.p25.P25DecodeEvent;
import io.github.dsheirer.protocol.Protocol;
public class MPT1327ChannelGrantEvent extends P25DecodeEvent
/**
* MPT1327 Channel Grant Event
*/
public class MPT1327ChannelGrantEvent extends MPT1327DecodeEvent
{
public MPT1327ChannelGrantEvent(long timestamp)
public MPT1327ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
super(timestamp);
super(decodeEventType, timestamp);
}
/**
@@ -37,9 +38,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(long timeStart)
public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(DecodeEventType decodeEventType, long timeStart)
{
return new MPT1327ChannelGrantDecodeEventBuilder(timeStart);
return new MPT1327ChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -49,7 +50,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
@@ -60,8 +60,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
*
* @param timeStart
*/
public MPT1327ChannelGrantDecodeEventBuilder(long timeStart)
public MPT1327ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -95,25 +96,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
return this;
}
/**
* Sets the Decode Event type for this event.
* @param eventType
*/
public MPT1327ChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public MPT1327ChannelGrantDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -140,13 +122,11 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
*/
public MPT1327ChannelGrantEvent build()
{
MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mTimeStart);
MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setProtocol(Protocol.MPT1327);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,23 +14,27 @@
*
* 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.mpt1327;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* MPT1327 Decode Event
*/
public class MPT1327DecodeEvent extends DecodeEvent
{
/**
* Constructs a MPT1327 decode event
* @param start
*/
public MPT1327DecodeEvent(long start)
public MPT1327DecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.MPT1327);
}
@@ -40,9 +43,9 @@ public class MPT1327DecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.MPT1327);
return decodeEventBuilder;
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -33,14 +33,13 @@ import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
import io.github.dsheirer.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MPT1327DecoderState extends DecoderState
{
@@ -198,9 +197,7 @@ public class MPT1327DecoderState extends DecoderState
private DecodeEvent getDecodeEvent(IMessage message, MPT1327Message mpt, DecodeEventType decodeEventType, String details)
{
MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
return MPT1327DecodeEvent.builder(message.getTimestamp())
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
return MPT1327DecodeEvent.builder(decodeEventType, message.getTimestamp())
.details(details)
.identifiers(ic)
.build();

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.mpt1327;
@@ -23,56 +22,55 @@ package io.github.dsheirer.module.decode.mpt1327;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageType;
import java.util.function.Function;
import java.util.*;
public class MPT1327MessageFilter extends Filter<IMessage>
/**
* Filter for MPT1327 messages
*/
public class MPT1327MessageFilter extends Filter<IMessage, MPT1327Message.MPTMessageType>
{
private Map<MPT1327Message.MPTMessageType, FilterElement<MPT1327Message.MPTMessageType>> mFilterElements = new EnumMap<>(MPT1327Message.MPTMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public MPT1327MessageFilter()
{
super("MPT1327 Message Type Filter");
super("MPT1327 Messages");
for(MPT1327Message.MPTMessageType type : MPT1327Message.MPTMessageType.values())
{
if(type != MPT1327Message.MPTMessageType.UNKN)
{
mFilterElements.put(type, new FilterElement<MPT1327Message.MPTMessageType>(type));
}
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
MPT1327Message mpt = (MPT1327Message) message;
FilterElement<MPT1327Message.MPTMessageType> element =
mFilterElements.get(mpt.getMessageType());
if(element != null)
{
return element.isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof MPT1327Message;
return message instanceof MPT1327Message && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, MPT1327Message.MPTMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mFilterElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage, MPT1327Message.MPTMessageType>
{
@Override
public MPT1327Message.MPTMessageType apply(IMessage message)
{
if(message instanceof MPT1327Message mpt)
{
return mpt.getMessageType();
}
return null;
}
}
}

View File

@@ -104,10 +104,9 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
}
}
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp())
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent
.mpt1327Builder(DecodeEventType.CALL, mpt1327Message.getTimestamp())
.channel(mpt1327Channel)
.eventType(DecodeEventType.CALL)
.eventDescription("Call")
.details("Traffic Channel Grant")
.identifiers(identifierCollection)
.build();
@@ -125,7 +124,7 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
channelGrantEvent.setEventDescription("Detect:" + channelGrantEvent.getEventDescription());
channelGrantEvent.setDetails("Detect:" + channelGrantEvent.getDetails());
return;
}
@@ -152,10 +151,8 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration();
List<Channel> trafficChannelList = new ArrayList<>();
if(decodeConfiguration instanceof DecodeConfigMPT1327)
if(decodeConfiguration instanceof DecodeConfigMPT1327 decodeConfigMPT1327)
{
DecodeConfigMPT1327 decodeConfigMPT1327 = (DecodeConfigMPT1327)decodeConfiguration;
int maxTrafficChannels = decodeConfigMPT1327.getTrafficChannelPoolSize();
for(int x = 0; x < maxTrafficChannels; x++)

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25;
@@ -29,9 +28,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
{
private ServiceOptions mServiceOptions;
public P25ChannelGrantEvent(long timestamp)
public P25ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
super(timestamp);
super(decodeEventType, timestamp);
}
/**
@@ -39,9 +38,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static P25ChannelGrantDecodeEventBuilder builder(long timeStart, ServiceOptions serviceOptions)
public static P25ChannelGrantDecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions)
{
return new P25ChannelGrantDecodeEventBuilder(timeStart, serviceOptions);
return new P25ChannelGrantDecodeEventBuilder(decodeEventType, timeStart, serviceOptions);
}
/**
@@ -76,7 +75,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
@@ -88,8 +86,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
*
* @param timeStart
*/
public P25ChannelGrantDecodeEventBuilder(long timeStart, ServiceOptions serviceOptions)
public P25ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
mServiceOptions = serviceOptions;
}
@@ -124,26 +123,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public P25ChannelGrantDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the {@link DecodeEventType}
* @param decodeEventType of the event
*/
public P25ChannelGrantDecodeEventBuilder eventType(DecodeEventType decodeEventType)
{
mDecodeEventType = decodeEventType;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -170,13 +149,11 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
*/
public P25ChannelGrantEvent build()
{
P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mTimeStart);
P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setProtocol(Protocol.APCO25);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setServiceOptions(mServiceOptions);
return decodeEvent;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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,12 +14,13 @@
*
* 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;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
public class P25DecodeEvent extends DecodeEvent
@@ -29,9 +29,9 @@ public class P25DecodeEvent extends DecodeEvent
* Constructs a P25 decode event
* @param start
*/
public P25DecodeEvent(long start)
public P25DecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.APCO25);
}
@@ -40,9 +40,9 @@ public class P25DecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.APCO25);
return decodeEventBuilder;
}

View File

@@ -281,11 +281,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Continue")
.details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.details("CONTINUE - PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -306,7 +304,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel != null)
{
event.setEventDescription(decodeEventType.toString());
event.setDetails("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""));
event.setChannelDescriptor(apco25Channel);
broadcast(event);
@@ -327,10 +324,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(mIgnoreDataCalls && opcode.isDataChannelGrant())
{
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Ignored")
.details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -340,10 +335,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
return;
}
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -357,8 +350,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED");
return;
}
@@ -427,11 +419,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Continue")
.details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.details("CONTINUE - PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -460,7 +450,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel != null)
{
event.setEventDescription(decodeEventType.toString());
event.setDetails("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""));
event.setChannelDescriptor(apco25Channel);
broadcast(event);
@@ -488,10 +477,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(mIgnoreDataCalls && opcode.isDataChannelGrant())
{
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Ignored")
.details("PHASE 2 DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -502,10 +489,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
return;
}
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -526,8 +511,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED");
return;
}
@@ -836,17 +820,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if (event != null)
{
event.setEventDescription(event.getEventDescription() + " - Rejected");
if (channelEvent.getDescription() != null)
{
event.setDetails(channelEvent.getDescription() + " - " + event.getDetails());
}
else
{
event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails());
}
event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails());
broadcast(event);
}
});

View File

@@ -654,10 +654,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
private void processBroadcast(List<Identifier> identifiers, long timestamp, DecodeEventType request, String s) {
MutableIdentifierCollection requestCollection = getMutableIdentifierCollection(identifiers);
broadcast(P25DecodeEvent.builder(timestamp)
broadcast(P25DecodeEvent.builder(request, timestamp)
.channel(getCurrentChannel())
.eventType(request)
.eventDescription(request.toString())
.details(s)
.identifiers(requestCollection)
.build());
@@ -767,17 +765,16 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
/**
* Updates or creates a current call event.
*
* @param type of call that will be used as an event description
* @param decodeEventType of call that will be used as an event description
* @param details of the call (optional)
* @param timestamp of the message indicating a call or continuation
*/
private void updateCurrentCall(DecodeEventType type, String details, long timestamp)
private void updateCurrentCall(DecodeEventType decodeEventType, String details, long timestamp)
{
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = P25DecodeEvent.builder(timestamp)
mCurrentCallEvent = P25DecodeEvent.builder(DecodeEventType.CALL, timestamp)
.channel(getCurrentChannel())
.eventDescription(type.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -791,9 +788,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
mCurrentCallEvent.end(timestamp);
broadcast(mCurrentCallEvent);
if(type == DecodeEventType.CALL_ENCRYPTED)
if(decodeEventType == DecodeEventType.CALL_ENCRYPTED)
{
mCurrentCallEvent.setEventDescription(type.toString());
mCurrentCallEvent.setDetails(details);
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ENCRYPTED));
}
@@ -838,12 +834,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processPDU(P25Message message)
{
if(message.isValid() && message instanceof PDUMessage)
if(message.isValid() && message instanceof PDUMessage pdu)
{
PDUMessage pdu = (PDUMessage)message;
processBroadcast(pdu.getIdentifiers(), message.getTimestamp(), DecodeEventType.DATA_PACKET, pdu.toString());
}
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
@@ -856,10 +849,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processUMBTC(P25Message message)
{
if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing)
if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing tired)
{
UMBTCTelephoneInterconnectRequestExplicitDialing tired = (UMBTCTelephoneInterconnectRequestExplicitDialing)message;
processBroadcast(tired.getIdentifiers(), tired.getTimestamp(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT:" + tired.getTelephoneNumber());
}
@@ -910,11 +901,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString())
.identifiers(ic)
.details(arsPacket.toString() + " " + ipv4.toString())
.details(arsPacket + " " + ipv4)
.build();
broadcast(packetEvent);
@@ -929,11 +919,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(DecodeEventType.CELLOCATOR, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription("Cellocator")
.identifiers(ic)
.details(mcgpPacket.toString() + " " + ipv4.toString())
.details(mcgpPacket + " " + ipv4)
.build();
broadcast(cellocatorEvent);
@@ -942,10 +931,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(packet.getIdentifiers());
DecodeEvent lrrpEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent lrrpEvent = P25DecodeEvent.builder(DecodeEventType.LRRP, message.getTimestamp())
.channel(getCurrentChannel())
.details(lrrpPacket + " " + ipv4)
.eventDescription("LRRP")
.identifiers(ic)
.protocol(Protocol.LRRP)
.build();
@@ -956,9 +944,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
if(geoPosition != null)
{
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(message.getTimestamp())
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent
.plottableBuilder(DecodeEventType.GPS, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
.identifiers(ic)
.protocol(Protocol.LRRP)
.location(geoPosition)
@@ -975,9 +963,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UDP_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.UDP_PACKET.toString())
.identifiers(ic)
.details(ipv4.toString())
.build();
@@ -993,9 +980,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.ICMP_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.ICMP_PACKET.toString())
.identifiers(ic)
.details(ipv4.toString())
.build();
@@ -1010,9 +996,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.IP_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.IP_PACKET.toString())
.identifiers(ic)
.details(ipv4.toString())
.build();
@@ -1028,9 +1013,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.UNKNOWN_PACKET.toString())
.identifiers(ic)
.details(packet.toString())
.build();
@@ -1376,9 +1360,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
private void processTSBKRoamingAddressRequest(TSBKMessage tsbk) {
//TODO: not sure if this should be used or not.
broadcast(P25DecodeEvent.builder(tsbk.getTimestamp())
broadcast(P25DecodeEvent.builder(DecodeEventType.REQUEST, tsbk.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.REQUEST.toString())
.details("ROAMING ADDRESS")
// TODO: This identifierCollection is different from all the others.
.identifiers(new IdentifierCollection(tsbk.getIdentifiers()))

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import java.util.Collections;
import java.util.List;
public class AMBTCMessageFilter extends Filter<IMessage>
{
public AMBTCMessageFilter()
{
super("Alternate Multi-Block Trunking Control");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof AMBTCMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,51 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class HDUMessageFilter extends Filter<IMessage>
/**
* Filter for HDU header messages
*/
public class HeaderMessageFilter extends Filter<IMessage,String>
{
public HDUMessageFilter()
{
super("Header Data Unit");
}
private static final String HDU_KEY = "Header Data Unit (HDU)";
private KeyExtractor mKeyExtractor = new KeyExtractor();
@Override
public boolean passes(IMessage message)
/**
* Constructor
*/
public HeaderMessageFilter()
{
return mEnabled && canProcess(message);
super("Header Messages");
add(new FilterElement<>(HDU_KEY));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof HDUMessage;
return message instanceof HDUMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, String> getKeyExtractor()
{
return Collections.EMPTY_LIST;
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof HDUMessage)
{
return HDU_KEY;
}
return null;
}
}
}

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
import java.util.Collections;
import java.util.List;
public class IPPacketMessageFilter extends Filter<IMessage>
{
public IPPacketMessageFilter()
{
super("Packet Message");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof PacketMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -23,33 +22,52 @@ package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class LDUMessageFilter extends Filter<IMessage>
/**
* Filter for unknown messages, meaning messages not handled by any other filter.
*/
public class P25OtherMessageFilter extends Filter<IMessage,String>
{
public LDUMessageFilter()
private static final String OTHER_KEY = "Other/Unknown Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public P25OtherMessageFilter()
{
super("Link Data Unit");
super("Other P25 messages");
add(new FilterElement<>(OTHER_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
return mEnabled && canProcess(message);
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LDUMessage;
return message instanceof P25Message && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return Collections.EMPTY_LIST;
@Override
public String apply(IMessage message)
{
if(message instanceof P25Message)
{
return OTHER_KEY;
}
return null;
}
}
}

View File

@@ -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,35 +14,42 @@
*
* 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.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.filter.SyncLossMessageFilter;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.SyncLossMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
public class P25MessageFilterSet extends FilterSet<IMessage>
/**
* Filter set for P25 messages
*/
public class P25P1MessageFilterSet extends FilterSet<IMessage>
{
public P25MessageFilterSet()
public P25P1MessageFilterSet()
{
super("P25 Message Filter");
addFilter(new AMBTCMessageFilter());
addFilter(new HDUMessageFilter());
addFilter(new IPPacketMessageFilter());
addFilter(new LDUMessageFilter());
addFilter(new HeaderMessageFilter());
addFilter(new PacketMessageFilter());
addFilter(new PDUMessageFilter());
addFilter(new SNDCPMessageFilter());
addFilter(new SyncLossMessageFilter());
addFilter(new TDUMessageFilter());
addFilter(new TDULCMessageFilter());
addFilter(new TSBKMessageFilterSet());
addFilter(new UMBTCMessageFilter());
addFilter(new TerminatorMessageFilterSet());
addFilter(new TrunkingOpcodeMessageFilterSet());
addFilter(new VoiceMessageFilter());
addFilter(new P25OtherMessageFilter());
}
/**
* Override default to descope handling to P25 or sync-loss messages.
* @param message to test
* @return true if the message can be processed
*/
@Override
public boolean canProcess(IMessage message)
{

View File

@@ -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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,49 +23,52 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.function.Function;
import java.util.*;
public class PDUMessageFilter extends Filter<IMessage>
/**
* Filter for PDU packet data unit messages
*/
public class PDUMessageFilter extends Filter<IMessage,String>
{
private Map<Opcode, FilterElement<Opcode>> mOpcodeFilterElements = new EnumMap<>(Opcode.class);
private static final String PDU_KEY = "Packet Data Unit (PDU)";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public PDUMessageFilter()
{
super("PDU - Packet Data Unit");
for(Opcode opcode : Opcode.values())
{
mOpcodeFilterElements.put(opcode, new FilterElement<Opcode>(opcode));
}
super("Packet Data Unit messages");
add(new FilterElement<>(PDU_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
if(mEnabled && message instanceof PDUMessage)
{
PDUMessage pdu = (PDUMessage) message;
Opcode opcode = pdu.getOpcode();
return mOpcodeFilterElements.get(opcode).isEnabled();
}
return false;
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof PDUMessage;
return message instanceof TDUMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return new ArrayList<FilterElement<?>>(mOpcodeFilterElements.values());
@Override
public String apply(IMessage message)
{
if(message instanceof PDUMessage)
{
return PDU_KEY;
}
return null;
}
}
}

View File

@@ -0,0 +1,135 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.ip.IPacket;
import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket;
import io.github.dsheirer.module.decode.ip.icmp.ICMPPacket;
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage;
import java.util.function.Function;
/**
* Filter for packet messages
*/
public class PacketMessageFilter extends Filter<IMessage,String>
{
private static final String KEY_CELLOCATOR = "Cellocator Packet";
private static final String KEY_IPV4_OTHER = "IPV4 Other Packet";
private static final String KEY_IPV4_UDP_ICMP = "IPV4 UDP/IP ICMP Packet";
private static final String KEY_IPV4_UDP_OTHER = "IPV4 UDP/IP Other Packet";
private static final String KEY_MOTOROLA_ARS = "Motorola Registration (ARS) Packet";
private static final String KEY_MOTOROLA_LRRP = "Motorola Location Report (LRRP) Packet";
private static final String KEY_MOTOROLA_XCMP = "Motorola Extensible (XCMP) Packet";
private static final String KEY_SNDCP = "SNDCP Data Packet";
private static final String KEY_UNKNOWN = "Unknown Packet";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public PacketMessageFilter()
{
super("Packet Messages");
add(new FilterElement<>(KEY_CELLOCATOR));
add(new FilterElement<>(KEY_IPV4_OTHER));
add(new FilterElement<>(KEY_IPV4_UDP_ICMP));
add(new FilterElement<>(KEY_IPV4_UDP_OTHER));
add(new FilterElement<>(KEY_MOTOROLA_ARS));
add(new FilterElement<>(KEY_MOTOROLA_LRRP));
add(new FilterElement<>(KEY_MOTOROLA_XCMP));
add(new FilterElement<>(KEY_SNDCP));
add(new FilterElement<>(KEY_UNKNOWN));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof PacketMessage && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof PacketMessage packetMessage)
{
IPacket packet = packetMessage.getPacket();
if(packet instanceof MCGPPacket)
{
return KEY_CELLOCATOR;
}
else if(packet instanceof ICMPPacket)
{
return KEY_IPV4_UDP_ICMP;
}
else if(packet instanceof UDPPacket)
{
return KEY_IPV4_UDP_OTHER;
}
else if(packet instanceof IPV4Packet)
{
return KEY_IPV4_OTHER;
}
else if(packet instanceof SNDCPPacketMessage)
{
return KEY_SNDCP;
}
else if(packet instanceof ARSPacket)
{
return KEY_MOTOROLA_ARS;
}
else if(packet instanceof LRRPPacket)
{
return KEY_MOTOROLA_LRRP;
}
else if(packet instanceof XCMPPacket)
{
return KEY_MOTOROLA_XCMP;
}
else
{
return KEY_UNKNOWN;
}
}
return null;
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,55 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPMessage;
import io.github.dsheirer.module.decode.p25.reference.PDUType;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class SNDCPMessageFilter extends Filter<IMessage>
/**
* Filter for SNDCP messages
*/
public class SNDCPMessageFilter extends Filter<IMessage, PDUType>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public SNDCPMessageFilter()
{
super("Sub-Network Dependent Convergence Protocol");
}
super("Sub-Network Dependent Convergence Protocol (SNDCP) Messages");
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
for(PDUType pduType: PDUType.values())
{
add(new FilterElement<>(pduType));
}
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof SNDCPMessage;
return message instanceof SNDCPMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, PDUType> getKeyExtractor()
{
return Collections.EMPTY_LIST;
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,PDUType>
{
@Override
public PDUType apply(IMessage message)
{
if(message instanceof SNDCPMessage sndcp)
{
return sndcp.getPDUType();
}
return null;
}
}
}

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import java.util.Collections;
import java.util.List;
public class TDULCMessageFilter extends Filter<IMessage>
{
public TDULCMessageFilter()
{
super("TDU Terminator Data Unit with Link Control");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof TDULinkControlMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,51 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class TDUMessageFilter extends Filter<IMessage>
/**
* Filter for TDU terminator (no link control) messages
*/
public class TDUMessageFilter extends Filter<IMessage,String>
{
private static final String TDU_KEY = "Terminator Data Unit (TDU)";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public TDUMessageFilter()
{
super("TDU Terminator Data Unit");
super("Terminator/No Link Control messages");
add(new FilterElement<>(TDU_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
return mEnabled && canProcess(message);
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof TDUMessage;
return message instanceof TDUMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return Collections.EMPTY_LIST;
@Override
public String apply(IMessage message)
{
if(message instanceof TDUMessage)
{
return TDU_KEY;
}
return null;
}
}
}

View File

@@ -1,113 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.*;
public class TSBKMessageFilterSet extends FilterSet<IMessage>
{
public TSBKMessageFilterSet()
{
super("TSBK Trunking Signalling Block");
addFilter(new StandardOpcodeFilter());
}
// @Override
// public boolean passes( Message message )
// {
// if( mEnabled && canProcess( message ) )
// {
// TSBKMessage tsbk = (TSBKMessage) message;
//
// switch( tsbk.getVendor() )
// {
// case STANDARD:
// return
// }
// Opcode opcode = tsbk.getOpcode();
//
// return mFilterElements.get( opcode ).isEnabled();
// }
//
// return false;
// }
@Override
public boolean canProcess(IMessage message)
{
return message instanceof TSBKMessage;
}
public class StandardOpcodeFilter extends Filter<IMessage>
{
private Map<Opcode, FilterElement<Opcode>> mStandardElements = new EnumMap<>(Opcode.class);
public StandardOpcodeFilter()
{
super("Standard Opcode Filter");
for(Opcode opcode : Opcode.values())
{
if(opcode != Opcode.OSP_UNKNOWN)
{
mStandardElements.put(opcode, new FilterElement<Opcode>(opcode));
}
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
Opcode opcode = ((TSBKMessage) message).getOpcode();
if(mStandardElements.containsKey(opcode))
{
return mStandardElements.get(opcode).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return ((TSBKMessage) message).getVendor() == Vendor.STANDARD;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>(mStandardElements.values());
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import java.util.Collection;
import java.util.function.Function;
/**
* Filter for TDU terminator and TDULC terminator with link control messages
*/
public class TerminatorMessageFilter extends Filter<IMessage, LinkControlOpcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public TerminatorMessageFilter(String name, Collection<LinkControlOpcode> opcodes)
{
super(name);
for(LinkControlOpcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
@Override
public Function<IMessage, LinkControlOpcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LinkControlOpcode>
{
@Override
public LinkControlOpcode apply(IMessage message)
{
if(message instanceof TDULinkControlMessage tdulc)
{
return tdulc.getLinkControlWord().getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for terminator/link control messages
*/
public class TerminatorMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public TerminatorMessageFilterSet()
{
super("Terminator Messages");
addFilter(new TDUMessageFilter());
addFilter(new TerminatorMessageFilter("Command/Status", LinkControlOpcode.COMMAND_STATUS_OPCODES));
addFilter(new TerminatorMessageFilter("Network/Channel", LinkControlOpcode.NETWORK_OPCODES));
addFilter(new TerminatorMessageFilter("Voice", LinkControlOpcode.VOICE_OPCODES));
addFilter(new TerminatorMessageFilter("Motorola", LinkControlOpcode.MOTOROLA_OPCODES));
List<LinkControlOpcode> others = new ArrayList<>();
for(LinkControlOpcode opcode: LinkControlOpcode.values())
{
if(!opcode.isGrouped())
{
others.add(opcode);
}
}
addFilter(new TerminatorMessageFilter("Other/Reserved", others));
}
@Override
public boolean canProcess(IMessage message)
{
return (message instanceof TDUMessage || message instanceof TDULinkControlMessage) && super.canProcess(message);
}
}

View File

@@ -0,0 +1,91 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import java.util.Collection;
import java.util.function.Function;
/**
* Message filter for P25 TSBK, AMBTC and UMBTC trunking control messages.
*/
public class TrunkingOpcodeMessageFilter extends Filter<IMessage, Opcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
private Collection<Opcode> mOpcodes;
/**
* Constructs an instance
*
* @param name of this filter
*/
public TrunkingOpcodeMessageFilter(String name, Collection<Opcode> opcodes)
{
super(name);
mOpcodes = opcodes;
for(Opcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
/**
* Key extractor
* @return opcode extractor
*/
@Override
public Function<IMessage, Opcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor for Opcode values from TSBK, AMBTC and UMBTC messages.
*/
private class KeyExtractor implements Function<IMessage,Opcode>
{
@Override
public Opcode apply(IMessage message)
{
if(message instanceof TSBKMessage tsbk)
{
return tsbk.getOpcode();
}
else if(message instanceof AMBTCMessage ambtc)
{
return ambtc.getHeader().getOpcode();
}
else if(message instanceof UMBTCMessage umbtc)
{
return umbtc.getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import java.util.List;
/**
* Filter set for trunking messages
*/
public class TrunkingOpcodeMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public TrunkingOpcodeMessageFilterSet()
{
super("Trunking Messages");
addFilter(new TrunkingOpcodeMessageFilter("Mobile Data Requests", Opcode.MOBILE_DATA_REQUESTS));
addFilter(new TrunkingOpcodeMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE));
addFilter(new TrunkingOpcodeMessageFilter("Mobile Voice Requests", Opcode.MOBILE_VOICE_REQUESTS));
addFilter(new TrunkingOpcodeMessageFilter("Network/Channel Status", Opcode.NETWORK_CHANNEL_STATUS));
addFilter(new TrunkingOpcodeMessageFilter("Network Data Grants", Opcode.NETWORK_DATA_GRANTS));
addFilter(new TrunkingOpcodeMessageFilter("Network Command/Request/Response", Opcode.NETWORK_COMMAND_REQUEST_RESPONSE));
addFilter(new TrunkingOpcodeMessageFilter("Network Voice Grants", Opcode.NETWORK_VOICE_GRANTS));
addFilter(new TrunkingOpcodeMessageFilter("Vendor-Harris Messages", Opcode.HARRIS));
addFilter(new TrunkingOpcodeMessageFilter("Vendor-Motorola Messages", Opcode.MOTOROLA));
addFilter(new TrunkingOpcodeMessageFilter("Vendor-Unknown Messages", Opcode.UNKNOWN));
List<Opcode> ungrouped = Opcode.getUngrouped();
if(!ungrouped.isEmpty())
{
addFilter(new TrunkingOpcodeMessageFilter("Unknown Opcode Messages", ungrouped));
}
}
@Override
public boolean canProcess(IMessage message)
{
return (message instanceof TSBKMessage || message instanceof AMBTCMessage || message instanceof UMBTCMessage) &&
super.canProcess(message);
}
}

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
import java.util.Collections;
import java.util.List;
public class UMBTCMessageFilter extends Filter<IMessage>
{
public UMBTCMessageFilter()
{
super("Unconfirmed Multi-Block Trunking Control");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof UMBTCMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -0,0 +1,83 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
import java.util.function.Function;
/**
* Filter for LDU1 and LDU2 voice messages
*/
public class VoiceMessageFilter extends Filter<IMessage,String>
{
private static final String LDU1_KEY = "LDU1 Voice Message";
private static final String LDU2_KEY = "LDU2 Voice Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public VoiceMessageFilter()
{
super("Voice Messages");
add(new FilterElement<>(LDU1_KEY));
add(new FilterElement<>(LDU2_KEY));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LDUMessage && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof LDU1Message)
{
return LDU1_KEY;
}
else if(message instanceof LDU2Message)
{
return LDU2_KEY;
}
return null;
}
}
}

View File

@@ -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,13 +14,17 @@
*
* 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;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.EnumSet;
/**
* Opcodes used for Link Control Words.
*/
public enum LinkControlOpcode
{
GROUP_VOICE_CHANNEL_USER("GROUP VOICE CHANNEL USER", 0),
@@ -101,22 +104,82 @@ public enum LinkControlOpcode
private String mLabel;
private int mCode;
/**
* Constructor
* @param label to display for the opcode
* @param code value.
*/
LinkControlOpcode(String label, int code)
{
mLabel = label;
mCode = code;
}
/**
* Command, response and status opcodes
*/
public static final EnumSet<LinkControlOpcode> COMMAND_STATUS_OPCODES = EnumSet.of(UNIT_TO_UNIT_ANSWER_REQUEST,
TELEPHONE_INTERCONNECT_ANSWER_REQUEST, CALL_TERMINATION_OR_CANCELLATION, GROUP_AFFILIATION_QUERY,
UNIT_AUTHENTICATION_COMMAND, UNIT_REGISTRATION_COMMAND, STATUS_QUERY, STATUS_UPDATE, MESSAGE_UPDATE,
EXTENDED_FUNCTION_COMMAND, CALL_ALERT);
/**
* Motorola Opcodes
*/
public static final EnumSet<LinkControlOpcode> MOTOROLA_OPCODES =
EnumSet.range(MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER, MOTOROLA_UNKNOWN);
/**
* Network/channel related opcodes
*/
public static final EnumSet<LinkControlOpcode> NETWORK_OPCODES = EnumSet.of(CHANNEL_IDENTIFIER_UPDATE,
CHANNEL_IDENTIFIER_UPDATE_EXPLICIT, SYSTEM_SERVICE_BROADCAST, SECONDARY_CONTROL_CHANNEL_BROADCAST,
SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, ADJACENT_SITE_STATUS_BROADCAST,
ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT, RFSS_STATUS_BROADCAST, RFSS_STATUS_BROADCAST_EXPLICIT,
NETWORK_STATUS_BROADCAST, NETWORK_STATUS_BROADCAST_EXPLICIT, PROTECTION_PARAMETER_BROADCAST);
/**
* Voice/call opcodes
*/
public static final EnumSet<LinkControlOpcode> VOICE_OPCODES = EnumSet.of(GROUP_VOICE_CHANNEL_USER,
GROUP_VOICE_CHANNEL_UPDATE, GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT, UNIT_TO_UNIT_VOICE_CHANNEL_USER,
TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER);
/**
* Indicates if the enumeration element is contained in one of the enumset groupings above.
* @return true if the element is grouped.
*/
public boolean isGrouped()
{
return COMMAND_STATUS_OPCODES.contains(this) || MOTOROLA_OPCODES.contains(this) ||
NETWORK_OPCODES.contains(this) || VOICE_OPCODES.contains(this);
}
/**
* Pretty label/string for the element
* @return label
*/
public String getLabel()
{
return mLabel;
}
/**
* Numeric value of the opcode.
* @return code value.
*/
public int getCode()
{
return mCode;
}
/**
* Lookup method for finding an opcode for a given vendor and value combination.
* @param value of the opcode
* @param vendor for the opcode
* @return matching element or UNKNOWN.
*/
public static LinkControlOpcode fromValue(int value, Vendor vendor)
{
switch(vendor)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2021 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
@@ -21,8 +21,9 @@ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk;
import io.github.dsheirer.module.decode.p25.reference.Direction;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
public enum Opcode
{
@@ -192,13 +193,12 @@ public enum Opcode
private String mLabel;
private String mDescription;
public static final EnumSet<Opcode> STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT,
OSP_PROTECTION_PARAMETER_UPDATE);
public static final EnumSet<Opcode> STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST,
ISP_RESERVED_3F);
public static final EnumSet<Opcode> DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(OSP_SNDCP_DATA_CHANNEL_GRANT,
OSP_INDIVIDUAL_DATA_CHANNEL_GRANT, OSP_GROUP_DATA_CHANNEL_GRANT);
/**
* Constructor
* @param code value
* @param label or pretty value.
* @param description of the opcode
*/
Opcode(int code, String label, String description)
{
mCode = code;
@@ -206,6 +206,135 @@ public enum Opcode
mDescription = description;
}
/**
* OSP standard opcodes
*/
public static final EnumSet<Opcode> STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT,
OSP_PROTECTION_PARAMETER_UPDATE);
/**
* ISP standard opcodes
*/
public static final EnumSet<Opcode> STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST,
ISP_RESERVED_3F);
/**
* ISP mobile data requests
*/
public static final EnumSet<Opcode> MOBILE_DATA_REQUESTS = EnumSet.of(ISP_INDIVIDUAL_DATA_SERVICE_REQUEST,
ISP_GROUP_DATA_SERVICE_REQUEST, ISP_SNDCP_DATA_CHANNEL_REQUEST, ISP_SNDCP_RECONNECT_REQUEST);
/**
* ISP mobile voice requests
*/
public static final EnumSet<Opcode> MOBILE_VOICE_REQUESTS = EnumSet.of(ISP_GROUP_VOICE_SERVICE_REQUEST,
ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST, ISP_TELEPHONE_INTERCONNECT_EXPLICIT_DIAL_REQUEST,
ISP_TELEPHONE_INTERCONNECT_PSTN_REQUEST);
/**
* ISP mobile request, response and status.
*/
public static final EnumSet<Opcode> MOBILE_REQUEST_RESPONSE = EnumSet.of(ISP_UNIT_TO_UNIT_ANSWER_RESPONSE,
ISP_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE, ISP_SNDCP_DATA_PAGE_RESPONSE, ISP_STATUS_UPDATE_REQUEST,
ISP_STATUS_QUERY_REQUEST, ISP_STATUS_QUERY_RESPONSE, ISP_MESSAGE_UPDATE_REQUEST,
ISP_RADIO_UNIT_MONITOR_REQUEST, ISP_CALL_ALERT_REQUEST, ISP_UNIT_ACKNOWLEDGE_RESPONSE,
ISP_CANCEL_SERVICE_REQUEST, ISP_EXTENDED_FUNCTION_RESPONSE, ISP_EMERGENCY_ALARM_REQUEST,
ISP_GROUP_AFFILIATION_QUERY_RESPONSE, ISP_GROUP_AFFILIATION_REQUEST, ISP_UNIT_DE_REGISTRATION_REQUEST,
ISP_UNIT_REGISTRATION_REQUEST, ISP_LOCATION_REGISTRATION_REQUEST, ISP_AUTHENTICATION_QUERY_OBSOLETE,
ISP_AUTHENTICATION_RESPONSE_OBSOLETE, ISP_PROTECTION_PARAMETER_REQUEST, ISP_IDENTIFIER_UPDATE_REQUEST,
ISP_ROAMING_ADDRESS_REQUEST, ISP_ROAMING_ADDRESS_RESPONSE, ISP_AUTHENTICATION_RESPONSE,
ISP_AUTHENTICATION_RESPONSE_MUTUAL, ISP_AUTHENTICATION_FNE_RESULT, ISP_AUTHENTICATION_SU_DEMAND);
/**
* OSP network data channel grants.
*/
public static final EnumSet<Opcode> NETWORK_DATA_GRANTS = EnumSet.of(OSP_SNDCP_DATA_CHANNEL_GRANT,
OSP_INDIVIDUAL_DATA_CHANNEL_GRANT, OSP_GROUP_DATA_CHANNEL_GRANT);
/**
* OSP network status
*/
public static final EnumSet<Opcode> NETWORK_CHANNEL_STATUS = EnumSet.of(OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT,
OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT, OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT,
OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, OSP_TDMA_SYNC_BROADCAST, OSP_IDENTIFIER_UPDATE_TDMA,
OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS, OSP_TIME_DATE_ANNOUNCEMENT, OSP_SYSTEM_SERVICE_BROADCAST,
OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST, OSP_RFSS_STATUS_BROADCAST, OSP_NETWORK_STATUS_BROADCAST,
OSP_ADJACENT_STATUS_BROADCAST, OSP_IDENTIFIER_UPDATE, OSP_PROTECTION_PARAMETER_BROADCAST,
OSP_PROTECTION_PARAMETER_UPDATE);
/**
* OSP network command, request and response
*/
public static final EnumSet<Opcode> NETWORK_COMMAND_REQUEST_RESPONSE = EnumSet.of(OSP_UNIT_TO_UNIT_ANSWER_REQUEST,
OSP_TELEPHONE_INTERCONNECT_ANSWER_REQUEST, OSP_SNDCP_DATA_PAGE_REQUEST, OSP_STATUS_UPDATE,
OSP_STATUS_QUERY, OSP_MESSAGE_UPDATE, OSP_RADIO_UNIT_MONITOR_COMMAND, OSP_CALL_ALERT,
OSP_ACKNOWLEDGE_RESPONSE, OSP_QUEUED_RESPONSE, OSP_EXTENDED_FUNCTION_COMMAND, OSP_DENY_RESPONSE,
OSP_GROUP_AFFILIATION_QUERY, OSP_GROUP_AFFILIATION_RESPONSE, OSP_LOCATION_REGISTRATION_RESPONSE,
OSP_UNIT_REGISTRATION_COMMAND, OSP_UNIT_REGISTRATION_RESPONSE, OSP_AUTHENTICATION_COMMAND,
OSP_UNIT_DEREGISTRATION_ACKNOWLEDGE, OSP_AUTHENTICATION_DEMAND, OSP_AUTHENTICATION_FNE_RESPONSE,
OSP_ROAMING_ADDRESS_COMMAND, OSP_ROAMING_ADDRESS_UPDATE);
/**
* OSP network voice channel grants.
*/
public static final EnumSet<Opcode> NETWORK_VOICE_GRANTS = EnumSet.of(OSP_GROUP_VOICE_CHANNEL_GRANT,
OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE, OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT,
OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT, OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE,
OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT, OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE);
/**
* Motorola opcodes
*/
public static final EnumSet<Opcode> MOTOROLA = EnumSet.of(MOTOROLA_ISP_UNKNOWN, MOTOROLA_OSP_PATCH_GROUP_ADD,
MOTOROLA_OSP_PATCH_GROUP_DELETE, MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT,
MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE, MOTOROLA_OSP_TRAFFIC_CHANNEL_ID,
MOTOROLA_OSP_DENY_RESPONSE, MOTOROLA_OSP_SYSTEM_LOADING, MOTOROLA_OSP_BASE_STATION_ID,
MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN, MOTOROLA_OSP_UNKNOWN);
/**
* Harris opcodes
*/
public static final EnumSet<Opcode> HARRIS = EnumSet.of(HARRIS_ISP_UNKNOWN, HARRIS_OSP_TDMA_SYNC,
HARRIS_OSP_UNKNOWN);
/**
* Unknown vendor ISP/OSP opcodes
*/
public static final EnumSet<Opcode> UNKNOWN = EnumSet.of(UNKNOWN_VENDOR_ISP, UNKNOWN_VENDOR_OSP);
/**
* Indicates if the enumeration element is contained in one of the enumset groupings above.
* @return true if the element is grouped.
*/
public boolean isGrouped()
{
return MOBILE_DATA_REQUESTS.contains(this) || MOBILE_VOICE_REQUESTS.contains(this) ||
MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_DATA_GRANTS.contains(this) ||
NETWORK_CHANNEL_STATUS.contains(this) || NETWORK_COMMAND_REQUEST_RESPONSE.contains(this) ||
NETWORK_VOICE_GRANTS.contains(this) || MOTOROLA.contains(this) || HARRIS.contains(this) ||
UNKNOWN.contains(this);
}
/**
* List of opcodes that are not grouped. This will catch any opcodes that are added to this enumeration where
* the opcode is not added to one of the existing groups. This is used for message filtering to ensure that all
* opcodes and handled by the filter/filterset.
* @return list of ungrouped opcodes.
*/
public static List<Opcode> getUngrouped()
{
List<Opcode> ungrouped = new ArrayList<>();
for(Opcode opcode: ungrouped)
{
if(!opcode.isGrouped())
{
ungrouped.add(opcode);
}
}
return ungrouped;
}
/**
* Formatted, fixed-length label
*/
@@ -235,7 +364,7 @@ public enum Opcode
*/
public boolean isDataChannelGrant()
{
return DATA_CHANNEL_GRANT_OPCODES.contains(this);
return NETWORK_DATA_GRANTS.contains(this);
}
@Override
@@ -244,17 +373,13 @@ public enum Opcode
return mLabel;
}
@Deprecated //use the ISP/OSP version of this method instead
public static Opcode fromValue(int value)
{
if(0 <= value && value <= 63)
{
return values()[value];
}
return OSP_UNKNOWN;
}
/**
* Lookup an opcode from a numeric value, vendor, and inbound/outbound direction.
* @param value for the opcode
* @param direction indicator - inbound (mobile) or outbound (tower)
* @param vendor identifier
* @return opcode or UNKNOWN
*/
public static Opcode fromValue(int value, Direction direction, Vendor vendor)
{
switch(vendor)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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
@@ -169,9 +169,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
{
if(message.isValid() && message.getTimeslot() == getTimeslot())
{
if(message instanceof MacMessage)
if(message instanceof MacMessage macMessage)
{
MacMessage macMessage = (MacMessage)message;
processMacMessage(macMessage);
MacPduType macPduType = macMessage.getMacPduType();
@@ -315,31 +314,26 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
processMacRelease(message, mac);
break;
case PHASE1_65_GROUP_VOICE_SERVICE_REQUEST:
if(mac instanceof GroupVoiceServiceRequest)
if(mac instanceof GroupVoiceServiceRequest gvsr)
{
GroupVoiceServiceRequest gvsr = (GroupVoiceServiceRequest)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "GROUP VOICE SERVICE " + gvsr.getServiceOptions());
}
break;
case PHASE1_68_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_ABBREVIATED:
if(mac instanceof UnitToUnitVoiceChannelGrantAbbreviated)
if(mac instanceof UnitToUnitVoiceChannelGrantAbbreviated uuvcga)
{
UnitToUnitVoiceChannelGrantAbbreviated uuvcga = (UnitToUnitVoiceChannelGrantAbbreviated)mac;
broadcast(message, mac, uuvcga.getChannel(), DecodeEventType.CALL_UNIT_TO_UNIT, "VOICE CHANNEL GRANT");
}
break;
case PHASE1_69_UNIT_TO_UNIT_ANSWER_REQUEST_ABBREVIATED:
if(mac instanceof UnitToUnitAnswerRequestAbbreviated)
if(mac instanceof UnitToUnitAnswerRequestAbbreviated uuara)
{
UnitToUnitAnswerRequestAbbreviated uuara = (UnitToUnitAnswerRequestAbbreviated)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "UNIT-TO-UNIT ANSWER REQUEST - " + uuara.getServiceOptions());
}
break;
case PHASE1_74_TELEPHONE_INTERCONNECT_ANSWER_REQUEST:
if(mac instanceof TelephoneInterconnectAnswerRequest)
if(mac instanceof TelephoneInterconnectAnswerRequest tiar)
{
TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT ANSWER REQUEST");
}
break;
@@ -350,9 +344,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
processSDCG(message, mac);
break;
case PHASE1_85_SNDCP_DATA_PAGE_REQUEST:
if(mac instanceof SNDCPDataPageRequest)
if(mac instanceof SNDCPDataPageRequest sdpr)
{
SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.PAGE, "SNDCP DATA PAGE " + sdpr.getServiceOptions());
}
break;
@@ -366,9 +359,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
break;
case PHASE1_92_MESSAGE_UPDATE_ABBREVIATED:
if(mac instanceof MessageUpdateAbbreviated)
if(mac instanceof MessageUpdateAbbreviated mua)
{
MessageUpdateAbbreviated mua = (MessageUpdateAbbreviated)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.SDM, "MESSAGE UPDATE - " + mua.getShortDataMessage());
}
break;
@@ -379,9 +371,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
processCallAlert(message, mac);
break;
case PHASE1_96_ACK_RESPONSE:
if(mac instanceof AcknowledgeResponse)
if(mac instanceof AcknowledgeResponse ar)
{
AcknowledgeResponse ar = (AcknowledgeResponse)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.RESPONSE, "ACKNOWLEDGE: " + ar.getServiceType());
}
break;
@@ -401,10 +392,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
broadcast(message, mac, getCurrentChannel(), DecodeEventType.COMMAND, "UNIT REGISTRATION");
break;
case PHASE1_197_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED:
if(mac instanceof UnitToUnitAnswerRequestExtended)
if(mac instanceof UnitToUnitAnswerRequestExtended uuare)
{
UnitToUnitAnswerRequestExtended uuare = (UnitToUnitAnswerRequestExtended)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "UNIT-TO-UNIT ANSWER REQUEST " + uuare.getServiceOptions());
}
break;
@@ -418,9 +407,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
broadcast(message, mac, getCurrentChannel(), DecodeEventType.STATUS, "STATUS QUERY");
break;
case PHASE1_220_MESSAGE_UPDATE_EXTENDED:
if(mac instanceof MessageUpdateExtended)
if(mac instanceof MessageUpdateExtended mue)
{
MessageUpdateExtended mue = (MessageUpdateExtended)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.SDM, "MESSAGE UPDATE - " + mue.getShortDataMessage());
}
break;
@@ -468,9 +456,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processQueuedResponse(MacMessage message, MacStructure mac) {
if(mac instanceof QueuedResponse)
if(mac instanceof QueuedResponse qr)
{
QueuedResponse qr = (QueuedResponse)mac;
broadcast(message, mac, DecodeEventType.RESPONSE,
"QUEUED - " + qr.getQueuedResponseServiceType() +
" REASON:" + qr.getQueuedResponseReason() + " ADDL:" + qr.getAdditionalInfo());
@@ -479,27 +466,24 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
private void processEFCA(MacMessage message, MacStructure mac) {
if(mac instanceof ExtendedFunctionCommand)
if(mac instanceof ExtendedFunctionCommand efc)
{
ExtendedFunctionCommand efc = (ExtendedFunctionCommand)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
"EXTENDED FUNCTION: " + efc.getExtendedFunction() + " ARGUMENTS:" + efc.getArguments());
}
}
private void processDenyResponse(MacMessage message, MacStructure mac) {
if(mac instanceof DenyResponse)
if(mac instanceof DenyResponse dr)
{
DenyResponse dr = (DenyResponse)mac;
broadcast(message, mac, DecodeEventType.RESPONSE,
"DENY: " + dr.getDeniedServiceType() + " REASON:" + dr.getDenyReason() + " ADDL:" + dr.getAdditionalInfo());
}
}
private void processRUMCExtended(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommandExtended)
if(mac instanceof RadioUnitMonitorCommandExtended rumce)
{
RadioUnitMonitorCommandExtended rumce = (RadioUnitMonitorCommandExtended)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
"RADIO UNIT MONITOR" + (rumce.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumce.getTransmitTime() + "MULTIPLIER:" + rumce.getTransmitMultiplier());
@@ -507,18 +491,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processSUE(MacMessage message, MacStructure mac) {
if(mac instanceof StatusUpdateExtended)
if(mac instanceof StatusUpdateExtended sue)
{
StatusUpdateExtended sue = (StatusUpdateExtended)mac;
broadcast(message, mac, DecodeEventType.STATUS,
"STATUS UPDATE - UNIT:" + sue.getUnitStatus() + " USER:" + sue.getUserStatus());
}
}
private void processEFCE(MacMessage message, MacStructure mac) {
if(mac instanceof ExtendedFunctionCommandExtended)
if(mac instanceof ExtendedFunctionCommandExtended efce)
{
ExtendedFunctionCommandExtended efce = (ExtendedFunctionCommandExtended)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
"EXTENDED FUNCTION: " + efce.getExtendedFunction() + " ARGUMENTS:" + efce.getArguments());
}
@@ -529,9 +511,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processRUMCE(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommandEnhanced)
if(mac instanceof RadioUnitMonitorCommandEnhanced rumc)
{
RadioUnitMonitorCommandEnhanced rumc = (RadioUnitMonitorCommandEnhanced)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
"RADIO UNIT MONITOR" + (rumc.isStealthMode() ? " (STEALTH)" : "") +
" ENCRYPTION:" + rumc.getEncryption() +
@@ -540,18 +521,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processSUA(MacMessage message, MacStructure mac) {
if(mac instanceof StatusUpdateAbbreviated)
if(mac instanceof StatusUpdateAbbreviated sua)
{
StatusUpdateAbbreviated sua = (StatusUpdateAbbreviated)mac;
broadcast(message, mac, DecodeEventType.STATUS,
"STATUS UPDATE - UNIT:" + sua.getUnitStatus() + " USER:" + sua.getUserStatus());
}
}
private void processSDCG(MacMessage message, MacStructure mac) {
if(mac instanceof SNDCPDataChannelGrant)
if(mac instanceof SNDCPDataChannelGrant sdcg)
{
SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)mac;
broadcast(message, mac,
sdcg.getServiceOptions().isEncrypted() ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL,
"SNDCP CHANNEL GRANT " + sdcg.getServiceOptions());
@@ -559,9 +538,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processRUMCA(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommand)
if(mac instanceof RadioUnitMonitorCommand rumc)
{
RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
"RADIO UNIT MONITOR" + (rumc.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumc.getTransmitTime() + " MULTIPLIER:" + rumc.getTransmitMultiplier());
@@ -569,9 +547,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processMacRelease(MacMessage message, MacStructure mac) {
if(mac instanceof MacRelease)
if(mac instanceof MacRelease mr)
{
MacRelease mr = (MacRelease)mac;
closeCurrentCallEvent(message.getTimestamp(), true, message.getMacPduType());
broadcast(message, mac, DecodeEventType.COMMAND,
(mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isTalkerPreemption() ? " BY USER" : ""));
@@ -579,18 +556,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processPCSQ(MacMessage message, MacStructure mac) {
if(mac instanceof PowerControlSignalQuality)
if(mac instanceof PowerControlSignalQuality pcsq)
{
PowerControlSignalQuality pcsq = (PowerControlSignalQuality)mac;
broadcast(message, mac, DecodeEventType.COMMAND,
"ADJUST TRANSMIT POWER - RF:" + pcsq.getRFLevel() + " BER:" + pcsq.getBitErrorRate());
}
}
private void processIPMWP(MacMessage message, MacStructure mac) {
if(mac instanceof IndividualPagingMessage)
if(mac instanceof IndividualPagingMessage ipm)
{
IndividualPagingMessage ipm = (IndividualPagingMessage)mac;
boolean priority = ipm.isTalkgroupPriority1() || ipm.isTalkgroupPriority2() ||
ipm.isTalkgroupPriority3() || ipm.isTalkgroupPriority4();
broadcast(message, mac, DecodeEventType.PAGE,
@@ -601,10 +576,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
private void broadcast(MacMessage message, MacStructure mac, IChannelDescriptor currentChannel, DecodeEventType eventType, String details) {
MutableIdentifierCollection collection = getUpdatedMutableIdentifierCollection(mac);
broadcast(P25DecodeEvent.builder(message.getTimestamp())
broadcast(P25DecodeEvent.builder(eventType, message.getTimestamp())
.channel(currentChannel)
.eventType(eventType)
.eventDescription(eventType.toString())
.details(details)
.identifiers(collection)
.build());
@@ -613,10 +586,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
private void broadcast(MacMessage message, MacStructure structure, DecodeEventType eventType, String details) {
MutableIdentifierCollection icQueuedResponse = getUpdatedMutableIdentifierCollection(structure);
broadcast(P25DecodeEvent.builder(message.getTimestamp())
broadcast(P25DecodeEvent.builder(eventType, message.getTimestamp())
.channel(getCurrentChannel())
.eventType(eventType)
.eventDescription(eventType.toString())
.details(details)
.identifiers(icQueuedResponse)
.build());
@@ -630,10 +601,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processUTUVCGUExtended(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended)
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue)
{
UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = (UnitToUnitVoiceChannelGrantUpdateExtended)mac;
if(isCurrentGroup(uuvcgue.getTargetAddress()))
{
broadcastCurrentChannel(uuvcgue.getChannel());
@@ -642,10 +611,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUExplicit(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateExplicit)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateExplicit gvcgue)
{
GroupVoiceChannelGrantUpdateExplicit gvcgue = (GroupVoiceChannelGrantUpdateExplicit)mac;
if(isCurrentGroup(gvcgue.getGroupAddress()))
{
broadcastCurrentChannel(gvcgue.getChannel());
@@ -654,10 +621,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processUTUVCGUAbbreviated(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantAbbreviated)
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantAbbreviated uuvcga)
{
UnitToUnitVoiceChannelGrantAbbreviated uuvcga = (UnitToUnitVoiceChannelGrantAbbreviated)mac;
if(isCurrentGroup(uuvcga.getSourceAddress()) || isCurrentGroup(uuvcga.getTargetAddress()))
{
broadcastCurrentChannel(uuvcga.getChannel());
@@ -666,10 +631,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUpdate(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdate)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdate gvcgu)
{
GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)mac;
if(isCurrentGroup(gvcgu.getGroupAddressA()))
{
broadcastCurrentChannel(gvcgu.getChannelA());
@@ -683,10 +646,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGAbbreviated(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantAbbreviated)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantAbbreviated gvcga)
{
GroupVoiceChannelGrantAbbreviated gvcga = (GroupVoiceChannelGrantAbbreviated)mac;
if(isCurrentGroup(gvcga.getGroupAddress()))
{
broadcastCurrentChannel(gvcga.getChannel());
@@ -695,10 +656,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUMExplicit(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultipleExplicit)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultipleExplicit gvcgume)
{
GroupVoiceChannelGrantUpdateMultipleExplicit gvcgume = (GroupVoiceChannelGrantUpdateMultipleExplicit)mac;
if(isCurrentGroup(gvcgume.getGroupAddressA()))
{
broadcastCurrentChannel(gvcgume.getChannelA());
@@ -712,10 +671,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUM(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultiple)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultiple gvcgum)
{
GroupVoiceChannelGrantUpdateMultiple gvcgum = (GroupVoiceChannelGrantUpdateMultiple)mac;
if(isCurrentGroup(gvcgum.getGroupAddressA()))
{
broadcastCurrentChannel(gvcgum.getChannelA());
@@ -752,10 +709,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
getIdentifierCollection().update(mPatchGroupManager.update(identifier));
}
if(mac instanceof TelephoneInterconnectVoiceChannelUser)
if(mac instanceof TelephoneInterconnectVoiceChannelUser tivcu)
{
TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)mac;
if(tivcu.getServiceOptions().isEncrypted())
{
updateCurrentCall(DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, null, message.getTimestamp());
@@ -787,10 +742,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
getIdentifierCollection().update(mPatchGroupManager.update(identifier));
}
if(mac instanceof UnitToUnitVoiceChannelUserExtended)
if(mac instanceof UnitToUnitVoiceChannelUserExtended uuvcue)
{
UnitToUnitVoiceChannelUserExtended uuvcue = (UnitToUnitVoiceChannelUserExtended)mac;
if(uuvcue.getServiceOptions().isEncrypted())
{
updateCurrentCall(DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED, null, message.getTimestamp());
@@ -1046,9 +999,13 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
{
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = P25DecodeEvent.builder(timestamp)
if(type == null)
{
type = DecodeEventType.CALL;
}
mCurrentCallEvent = P25DecodeEvent.builder(type, timestamp)
.channel(getCurrentChannel())
.eventDescription(type != null ? type.toString() : DecodeEventType.CALL.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -1066,11 +1023,6 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
else
{
if(type != null)
{
mCurrentCallEvent.setEventDescription(type.toString());
}
if(details != null)
{
mCurrentCallEvent.setDetails(details);

View File

@@ -1,28 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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;
public class P25MessageFactory
{
}

View File

@@ -0,0 +1,81 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase2.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacOpcode;
import java.util.Collection;
import java.util.function.Function;
/**
* Message filter for P25 Phase 2 MAC opcode messages.
*/
public class MacOpcodeMessageFilter extends Filter<IMessage, MacOpcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
private Collection<MacOpcode> mOpcodes;
/**
* Constructs an instance
*
* @param name of this filter
*/
public MacOpcodeMessageFilter(String name, Collection<MacOpcode> opcodes)
{
super(name);
mOpcodes = opcodes;
for(MacOpcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
/**
* Key extractor
* @return opcode extractor
*/
@Override
public Function<IMessage, MacOpcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor for MacOpcode values from MacMessage.
*/
private class KeyExtractor implements Function<IMessage,MacOpcode>
{
@Override
public MacOpcode apply(IMessage message)
{
if(message instanceof MacMessage mac)
{
return mac.getMacStructure().getOpcode();
}
return null;
}
}
}

Some files were not shown because too many files have changed in this diff Show More