From 6a0402d1cf5740b7eb51042dc5d315f5d89c9e2f Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Fri, 13 Dec 2019 09:01:40 -0500 Subject: [PATCH] Resolves #609 P25 Phase 2 call events are not chaining together and are causing streaming events in the event panel. (#614) --- .../decode/p25/P25TrafficChannelManager.java | 130 ++++++++++++++---- .../p25/identifier/channel/P25Channel.java | 24 +++- .../decode/p25/phase2/P25P2DecoderState.java | 18 +-- .../source/config/SourceConfigTuner.java | 39 ++++-- 4 files changed, 150 insertions(+), 61 deletions(-) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java index 5bfac322..91256415 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java @@ -98,7 +98,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv private List mManagedPhase2TrafficChannels; private Map mAllocatedTrafficChannelMap = new ConcurrentHashMap<>(); - private Map mChannelGrantEventMap = new ConcurrentHashMap<>(); + private Map mPhase1ChannelGrantEventMap = new ConcurrentHashMap<>(); + private Map mPhase2TS0ChannelGrantEventMap = new ConcurrentHashMap<>(); + private Map mPhase2TS1ChannelGrantEventMap = new ConcurrentHashMap<>(); private Listener mChannelEventListener; private Listener mDecodeEventListener; @@ -271,7 +273,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv IdentifierCollection identifierCollection, Opcode opcode, long timestamp) { - P25ChannelGrantEvent event = mChannelGrantEventMap.get(apco25Channel); + P25ChannelGrantEvent event = mPhase1ChannelGrantEventMap.get(apco25Channel); if(event != null && isSameCall(identifierCollection, event.getIdentifierCollection())) { @@ -287,11 +289,11 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) .channel(apco25Channel) .eventDescription(getEventType(opcode, serviceOptions).toString() + " - Continue") - .details("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); - mChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + mPhase1ChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); broadcast(continuationGrantEvent); } } @@ -309,7 +311,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv if(trafficChannel != null) { event.setEventDescription(getEventType(opcode, serviceOptions).toString()); - event.setDetails("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")); + event.setDetails("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")); event.setChannelDescriptor(apco25Channel); broadcast(event); SourceConfigTuner sourceConfig = new SourceConfigTuner(); @@ -328,11 +330,11 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) .channel(apco25Channel) .eventDescription(getEventType(opcode, serviceOptions).toString() + " - Ignored") - .details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); - mChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mPhase1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); broadcast(channelGrantEvent); return; @@ -341,11 +343,11 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) .channel(apco25Channel) .eventDescription(getEventType(opcode, serviceOptions).toString()) - .details("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); - mChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mPhase1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); //Allocate a traffic channel for the downlink frequency if one isn't already allocated //NOTE: we could also allocate a traffic channel for the uplink frequency here, in the future @@ -388,9 +390,25 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv ((MutableIdentifierCollection)identifierCollection).silentUpdate(ScrambleParameterIdentifier.create(mPhase2ScrambleParameters)); } - P25ChannelGrantEvent event = mChannelGrantEventMap.get(apco25Channel); + int timeslot = apco25Channel.getTimeslot(); - identifierCollection.setTimeslot(apco25Channel.getTimeslot()); + P25ChannelGrantEvent event = null; + + if(timeslot == 0) + { + event = mPhase2TS0ChannelGrantEventMap.get(apco25Channel); + } + else if(timeslot == 1) + { + event = mPhase2TS1ChannelGrantEventMap.get(apco25Channel); + } + else + { + mLog.error("Ignoring: Invalid timeslot [" + timeslot + "] detected for P25 Phase 2 Channel Grant."); + return; + } + + identifierCollection.setTimeslot(timeslot); if(event != null && isSameCall(identifierCollection, event.getIdentifierCollection())) { @@ -406,11 +424,19 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) .channel(apco25Channel) .eventDescription(getEventType(opcode, serviceOptions).toString() + " - Continue") - .details("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); - mChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + if(timeslot == 0) + { + mPhase2TS0ChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + } + else + { + mPhase2TS1ChannelGrantEventMap.put(apco25Channel, continuationGrantEvent); + } + broadcast(continuationGrantEvent); } } @@ -429,7 +455,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv if(trafficChannel != null) { event.setEventDescription(getEventType(opcode, serviceOptions).toString()); - event.setDetails("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")); + event.setDetails("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")); event.setChannelDescriptor(apco25Channel); broadcast(event); SourceConfigTuner sourceConfig = new SourceConfigTuner(); @@ -456,11 +482,11 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) .channel(apco25Channel) .eventDescription(getEventType(opcode, serviceOptions).toString() + " - Ignored") - .details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .details("PHASE 2 DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); - mChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + mPhase1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); broadcast(channelGrantEvent); return; @@ -469,11 +495,18 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) .channel(apco25Channel) .eventDescription(getEventType(opcode, serviceOptions).toString()) - .details("CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "UNKNOWN SERVICE OPTIONS")) + .details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); - mChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + if(timeslot == 0) + { + mPhase2TS0ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + } + else + { + mPhase2TS1ChannelGrantEventMap.put(apco25Channel, channelGrantEvent); + } //Allocate a traffic channel for the downlink frequency if one isn't already allocated //NOTE: we could also allocate a traffic channel for the uplink frequency here, in the future @@ -735,6 +768,53 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv */ public class TrafficChannelTeardownMonitor implements Listener { + /** + * Removes any call events that are associated with the specified channel. + * @param channel that was removed + */ + private void cleanupCallEvents(Channel channel) + { + if(channel.getSourceConfiguration() instanceof SourceConfigTuner) + { + long frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency(); + + if(channel.getDecodeConfiguration().getDecoderType() == DecoderType.P25_PHASE1) + { + List channels = new ArrayList<>(mPhase1ChannelGrantEventMap.keySet()); + + for(APCO25Channel p25Channel: channels) + { + if(p25Channel.getDownlinkFrequency() == frequency) + { + mPhase1ChannelGrantEventMap.remove(p25Channel); + } + } + } + else + { + List channels = new ArrayList<>(mPhase2TS0ChannelGrantEventMap.keySet()); + + for(APCO25Channel p25Channel: channels) + { + if(p25Channel.getDownlinkFrequency() == frequency) + { + mPhase2TS0ChannelGrantEventMap.remove(p25Channel); + } + } + + channels = new ArrayList<>(mPhase2TS1ChannelGrantEventMap.keySet()); + + for(APCO25Channel p25Channel: channels) + { + if(p25Channel.getDownlinkFrequency() == frequency) + { + mPhase2TS1ChannelGrantEventMap.remove(p25Channel); + } + } + } + } + } + @Override public synchronized void receive(ChannelEvent channelEvent) { @@ -773,15 +853,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv { mAvailablePhase2TrafficChannelQueue.add(channel); } - - P25ChannelGrantEvent event = mChannelGrantEventMap.remove(toRemove); - - if(event != null) - { - event.end(System.currentTimeMillis()); - broadcast(event); - } } + + cleanupCallEvents(channel); break; case NOTIFICATION_PROCESSING_START_REJECTED: APCO25Channel rejected = null; @@ -808,7 +882,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv mAvailablePhase2TrafficChannelQueue.add(channel); } - P25ChannelGrantEvent event = mChannelGrantEventMap.get(rejected); + P25ChannelGrantEvent event = mPhase1ChannelGrantEventMap.get(rejected); if(event != null) { @@ -826,6 +900,8 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv broadcast(event); } } + + cleanupCallEvents(channel); break; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java index e157ae27..8bee3c1a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/identifier/channel/P25Channel.java @@ -184,19 +184,37 @@ public class P25Channel implements IChannelDescriptor */ public String toString() { + StringBuilder sb = new StringBuilder(); + if(getDownlinkBandIdentifier() == getUplinkBandIdentifier() && getDownlinkChannelNumber() == getUplinkChannelNumber()) { - return getDownlinkBandIdentifier() + "-" + (getDownlinkLogicalChannelNumber()); + sb.append(getDownlinkBandIdentifier()).append("-").append(getDownlinkLogicalChannelNumber()); } else if(hasUplinkChannel()) { - return getDownlinkBandIdentifier() + "-" + (getDownlinkLogicalChannelNumber()) + "/" + - getUplinkBandIdentifier() + "-" + (getUplinkLogicalChannelNumber()); + sb.append(getDownlinkBandIdentifier()) + .append("-") + .append(getDownlinkLogicalChannelNumber()) + .append("/") + .append(getUplinkBandIdentifier()) + .append("-") + .append(getUplinkLogicalChannelNumber()); } else { + sb.append(getDownlinkBandIdentifier()) + .append("-") + .append(getDownlinkLogicalChannelNumber()) + .append("/-----"); return getDownlinkBandIdentifier() + "-" + (getDownlinkLogicalChannelNumber()) + "/-----"; } + + if(isTDMAChannel()) + { + sb.append(" TS").append(getTimeslot()); + } + + return sb.toString(); } /** diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java index b673531c..2d76708a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java @@ -28,8 +28,6 @@ import io.github.dsheirer.channel.state.State; import io.github.dsheirer.channel.state.TimeslotDecoderState; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.Channel.ChannelType; -import io.github.dsheirer.controller.channel.ChannelEvent; -import io.github.dsheirer.controller.channel.IChannelEventListener; import io.github.dsheirer.identifier.Form; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.IdentifierClass; @@ -87,7 +85,6 @@ import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToU import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToUnitVoiceChannelUserAbbreviated; import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.UnitToUnitVoiceChannelUserExtended; import io.github.dsheirer.module.decode.p25.phase2.timeslot.AbstractVoiceTimeslot; -import io.github.dsheirer.sample.Listener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,14 +93,13 @@ import org.slf4j.LoggerFactory; * by monitoring the decoded message stream. * */ -public class P25P2DecoderState extends TimeslotDecoderState implements IChannelEventListener, IdentifierUpdateListener +public class P25P2DecoderState extends TimeslotDecoderState implements IdentifierUpdateListener { private final static Logger mLog = LoggerFactory.getLogger(P25P2DecoderState.class); private ChannelType mChannelType; private PatchGroupManager mPatchGroupManager = new PatchGroupManager(); private P25P2NetworkConfigurationMonitor mNetworkConfigurationMonitor = new P25P2NetworkConfigurationMonitor(); - private Listener mChannelEventListener; private DecodeEvent mCurrentCallEvent; /** @@ -125,16 +121,6 @@ public class P25P2DecoderState extends TimeslotDecoderState implements IChannelE return DecoderType.P25_PHASE2; } - /** - * Implements the IChannelEventListener interface to receive traffic channel teardown notifications so that the - * traffic channel manager can manage traffic channel allocations. - */ - @Override - public Listener getChannelEventListener() - { - return mChannelEventListener; - } - /** * Performs a full reset to prepare this object for reuse on a new channel */ @@ -145,7 +131,7 @@ public class P25P2DecoderState extends TimeslotDecoderState implements IChannelE resetState(); } -/** + /** * Resets any temporal state details */ protected void resetState() diff --git a/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java b/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java index 9c397320..6cf501ed 100644 --- a/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java +++ b/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java @@ -1,21 +1,23 @@ /* - * ****************************************************************************** - * 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. + * * ****************************************************************************** + * * 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 + * * ***************************************************************************** * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * ***************************************************************************** */ package io.github.dsheirer.source.config; @@ -89,7 +91,8 @@ public class SourceConfigTuner extends SourceConfiguration @Override public String getDescription() { - return FREQUENCY_FORMAT.format((double)mFrequency / 1000000.0d) + " MHz"; + return FREQUENCY_FORMAT.format((double)mFrequency / 1000000.0d) + + (mPreferredTuner != null ? "MHz PREFERRED TUNER:" + mPreferredTuner : "MHz"); } @JsonIgnore @@ -97,4 +100,10 @@ public class SourceConfigTuner extends SourceConfiguration { return new TunerChannel(mFrequency, bandwidth); } + + @Override + public String toString() + { + return getDescription(); + } }