Resolves #609 P25 Phase 2 call events are not chaining together and are causing streaming events in the event panel. (#614)

This commit is contained in:
Denny Sheirer
2019-12-13 09:01:40 -05:00
committed by GitHub
parent 5309af84f7
commit 6a0402d1cf
4 changed files with 150 additions and 61 deletions

View File

@@ -98,7 +98,9 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv
private List<Channel> mManagedPhase2TrafficChannels;
private Map<APCO25Channel,Channel> mAllocatedTrafficChannelMap = new ConcurrentHashMap<>();
private Map<APCO25Channel,P25ChannelGrantEvent> mChannelGrantEventMap = new ConcurrentHashMap<>();
private Map<APCO25Channel,P25ChannelGrantEvent> mPhase1ChannelGrantEventMap = new ConcurrentHashMap<>();
private Map<APCO25Channel,P25ChannelGrantEvent> mPhase2TS0ChannelGrantEventMap = new ConcurrentHashMap<>();
private Map<APCO25Channel,P25ChannelGrantEvent> mPhase2TS1ChannelGrantEventMap = new ConcurrentHashMap<>();
private Listener<ChannelEvent> mChannelEventListener;
private Listener<IDecodeEvent> 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<ChannelEvent>
{
/**
* 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<APCO25Channel> channels = new ArrayList<>(mPhase1ChannelGrantEventMap.keySet());
for(APCO25Channel p25Channel: channels)
{
if(p25Channel.getDownlinkFrequency() == frequency)
{
mPhase1ChannelGrantEventMap.remove(p25Channel);
}
}
}
else
{
List<APCO25Channel> 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;
}
}

View File

@@ -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();
}
/**

View File

@@ -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<ChannelEvent> 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<ChannelEvent> 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()

View File

@@ -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 <http://www.gnu.org/licenses/>
* * *****************************************************************************
*
* 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.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();
}
}