Merge pull request #391 from DSheirer/389-add-active-to-channel-states

389 Add ACTIVE Channel State and Update P25 Decoder State processing
This commit is contained in:
Denny Sheirer
2018-08-11 05:39:20 -04:00
committed by GitHub
5 changed files with 195 additions and 147 deletions

View File

@@ -123,6 +123,11 @@ public class AttributeMonitor<T>
{ {
mCurrentValue = null; mCurrentValue = null;
mOccurrenceCounts.clear(); mOccurrenceCounts.clear();
if(mListener != null)
{
mListener.receive(new AttributeChangeRequest(mAttribute, null, null));
}
} }
/** /**

View File

@@ -25,32 +25,36 @@ import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.controller.channel.ChannelUtils; import io.github.dsheirer.controller.channel.ChannelUtils;
import io.github.dsheirer.icon.IconManager; import io.github.dsheirer.icon.IconManager;
import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*; import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*; import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
public class ChannelMetadataPanel extends JPanel implements ListSelectionListener public class ChannelMetadataPanel extends JPanel implements ListSelectionListener
{ {
private final static Logger mLog = LoggerFactory.getLogger(ChannelMetadataPanel.class);
private static final String PROPERTY_PREFIX_BACKGROUND = "channel.metadata.panel.state.color.";
private static final String PROPERTY_PREFIX_FOREGROUND = "channel.metadata.panel.text.color.";
private ChannelProcessingManager mChannelProcessingManager; private ChannelProcessingManager mChannelProcessingManager;
private IconManager mIconManager; private IconManager mIconManager;
private JTable mTable; private JTable mTable;
private Broadcaster<ProcessingChain> mSelectedProcessingChainBroadcaster = new Broadcaster<>(); private Broadcaster<ProcessingChain> mSelectedProcessingChainBroadcaster = new Broadcaster<>();
private Map<State,Color> mBackgroundColors = new HashMap<>();
private Map<State,Color> mForegroundColors = new HashMap<>();
/** /**
* Table view for currently decoding channel metadata * Table view for currently decoding channel metadata
@@ -94,7 +98,34 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
add(scrollPane); add(scrollPane);
// new TableColumnWidthMonitor(mTable, PROPERTY_PREFIX, new int[] {15,15,15,15,25,25,25,25,15,40}); setColors();
}
/**
* Setup the background and foreground color palette for the various channel states.
*/
private void setColors()
{
mBackgroundColors.put(State.ACTIVE, Color.CYAN);
mForegroundColors.put(State.ACTIVE, Color.YELLOW);
mBackgroundColors.put(State.CALL, Color.BLUE);
mForegroundColors.put(State.CALL, Color.YELLOW);
mBackgroundColors.put(State.CONTROL, Color.ORANGE);
mForegroundColors.put(State.CONTROL, Color.BLUE);
mBackgroundColors.put(State.DATA, Color.GREEN);
mForegroundColors.put(State.DATA, Color.BLUE);
mBackgroundColors.put(State.ENCRYPTED, Color.MAGENTA);
mForegroundColors.put(State.ENCRYPTED, Color.WHITE);
mBackgroundColors.put(State.FADE, Color.LIGHT_GRAY);
mForegroundColors.put(State.FADE, Color.DARK_GRAY);
mBackgroundColors.put(State.IDLE, Color.WHITE);
mForegroundColors.put(State.IDLE, Color.DARK_GRAY);
mBackgroundColors.put(State.OVERFLOW, Color.RED);
mForegroundColors.put(State.OVERFLOW, Color.YELLOW);
mBackgroundColors.put(State.RESET, Color.PINK);
mForegroundColors.put(State.RESET, Color.YELLOW);
mBackgroundColors.put(State.TEARDOWN, Color.DARK_GRAY);
mForegroundColors.put(State.TEARDOWN, Color.WHITE);
} }
@Override @Override
@@ -185,44 +216,14 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
State state = (State)value; State state = (State)value;
label.setText(state.getDisplayValue()); label.setText(state.getDisplayValue());
switch(state) if(mBackgroundColors.containsKey(state))
{ {
case CALL: background = mBackgroundColors.get(state);
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "call", Color.BLUE); }
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "call", Color.YELLOW);
break; if(mForegroundColors.containsKey(state))
case CONTROL: {
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "control", Color.ORANGE); foreground = mForegroundColors.get(state);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "control", Color.BLUE);
break;
case DATA:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "data", Color.GREEN);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "data", Color.BLUE);
break;
case ENCRYPTED:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "encrypted", Color.MAGENTA);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "encrypted", Color.WHITE);
break;
case FADE:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "fade", Color.LIGHT_GRAY.BLUE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "fade", Color.YELLOW);
break;
case IDLE:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
break;
case OVERFLOW:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "overflow", Color.RED);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "overflow", Color.YELLOW);
break;
case RESET:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
break;
case TEARDOWN:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "teardown", Color.DARK_GRAY);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "teardown", Color.WHITE);
break;
} }
} }
else else

View File

@@ -303,6 +303,11 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
{ {
switch(state) switch(state)
{ {
case ACTIVE:
broadcast(SquelchState.SQUELCH);
updateFadeTimeout();
mState = state;
break;
case CONTROL: case CONTROL:
//Don't allow traffic channels to be control channels, otherwise they can't transition to teardown //Don't allow traffic channels to be control channels, otherwise they can't transition to teardown
if(isStandardChannel()) if(isStandardChannel())

View File

@@ -24,54 +24,114 @@ import java.util.EnumSet;
*/ */
public enum State public enum State
{ {
IDLE("IDLE") /**
* Active state indicates that valid messages are being decoded that do not indicate any call or data
* activity. For example, this is used for P25 when a channel is actively transmitting Terminator Data
* Units in between data packets or at the beginning or end of a call.
*/
ACTIVE("ACTIVE")
{ {
@Override @Override
public boolean canChangeTo(State state) public boolean canChangeTo(State state)
{ {
return state != TEARDOWN && state != RESET && state != OVERFLOW; return state == CALL |
state == CONTROL ||
state == DATA ||
state == ENCRYPTED ||
state == FADE ||
state == TEARDOWN;
} }
}, },
/**
* Indicates that call messages are being decoded, or a call is in progress (ie producing audio).
*/
CALL("CALL") CALL("CALL")
{ {
@Override @Override
public boolean canChangeTo(State state) public boolean canChangeTo(State state)
{ {
return state == CONTROL || state == DATA || state == ENCRYPTED || state == FADE || state == TEARDOWN; return state == ACTIVE ||
} state == CONTROL ||
}, state == DATA ||
DATA("DATA") state == ENCRYPTED ||
{ state == FADE ||
@Override state == TEARDOWN;
public boolean canChangeTo(State state)
{
return state == CALL || state == CONTROL || state == ENCRYPTED || state == FADE || state == TEARDOWN;
}
},
ENCRYPTED("ENCRYPTED")
{
@Override
public boolean canChangeTo(State state)
{
return state == FADE || state == TEARDOWN;
} }
}, },
/**
* Indicates a trunking control channel is being decoded.
*/
CONTROL("CONTROL") CONTROL("CONTROL")
{ {
@Override @Override
public boolean canChangeTo(State state) public boolean canChangeTo(State state)
{ {
return state == IDLE || state == FADE; return state == IDLE ||
state == FADE;
} }
}, },
/**
* Indicates data packets are being decoded
*/
DATA("DATA")
{
@Override
public boolean canChangeTo(State state)
{
return state == ACTIVE ||
state == CALL ||
state == CONTROL ||
state == ENCRYPTED ||
state == FADE ||
state == TEARDOWN;
}
},
/**
* Indicates encrypted audio is detected.
*/
ENCRYPTED("ENCRYPTED")
{
@Override
public boolean canChangeTo(State state)
{
return state == FADE ||
state == TEARDOWN;
}
},
/**
* Fade state occurs when the channel is no longer actively decoding an messages. This is a transitory
* state before transition to IDLE or back to an active state.
*/
FADE("FADE") FADE("FADE")
{ {
@Override @Override
public boolean canChangeTo(State state) public boolean canChangeTo(State state)
{ {
return state != FADE && state != RESET && state != OVERFLOW; return state != FADE &&
state != RESET &&
state != OVERFLOW;
} }
}, },
/**
* Indicates the channel is idle and not actively decoding any messages
*/
IDLE("IDLE")
{
@Override
public boolean canChangeTo(State state)
{
return state != TEARDOWN &&
state != RESET &&
state != OVERFLOW;
}
},
/**
* Indicates that the channel's sample buffer has overflowed and new buffer samples are no longer being
* delivered to the channel. This state persists until the processing catches up and reduces the number
* of samples in the input buffer below a threshold value. Transition to and from this state is exclusively
* managed by the Channel State and no other channel states can be processed from the channel decoders
* until the channel state is taken out of overflow.
*/
OVERFLOW("OVERFLOW") OVERFLOW("OVERFLOW")
{ {
@Override @Override
@@ -80,6 +140,9 @@ public enum State
return false; return false;
} }
}, },
/**
* Indicates that the channel has been reset and is ready to use for a new decoding session.
*/
RESET("RESET") RESET("RESET")
{ {
@Override @Override
@@ -88,6 +151,10 @@ public enum State
return state == IDLE; return state == IDLE;
} }
}, },
/**
* Call or data teardown. This state is used by traffic channels to indicate that a call or data session
* has concluded and the channel is ready for tear down and reuse.
*/
TEARDOWN("TEARDOWN") TEARDOWN("TEARDOWN")
{ {
@Override @Override
@@ -99,7 +166,7 @@ public enum State
private String mDisplayValue; private String mDisplayValue;
public static final EnumSet<State> CALL_STATES = EnumSet.of(CALL, CONTROL, DATA, ENCRYPTED); public static final EnumSet<State> CALL_STATES = EnumSet.of(ACTIVE, CALL, CONTROL, DATA, ENCRYPTED);
public static final EnumSet<State> IDLE_STATES = EnumSet.of(IDLE, FADE); public static final EnumSet<State> IDLE_STATES = EnumSet.of(IDLE, FADE);
private State(String displayValue) private State(String displayValue)

View File

@@ -254,6 +254,7 @@ public class P25DecoderState extends DecoderState
if(mCurrentCallEvent != null) if(mCurrentCallEvent != null)
{ {
mCurrentCallEvent.end(); mCurrentCallEvent.end();
broadcast(mCurrentCallEvent);
} }
mCurrentCallEvent = null; mCurrentCallEvent = null;
@@ -395,6 +396,10 @@ public class P25DecoderState extends DecoderState
*/ */
private void processTDU(TDUMessage tdu) private void processTDU(TDUMessage tdu)
{ {
if(mChannelType == ChannelType.STANDARD)
{
broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE));
}
} }
/** /**
@@ -432,19 +437,21 @@ public class P25DecoderState extends DecoderState
*/ */
private void processTDULC(TDULinkControlMessage tdulc) private void processTDULC(TDULinkControlMessage tdulc)
{ {
if(mCurrentCallEvent != null)
{
mCurrentCallEvent.end();
broadcast(mCurrentCallEvent);
mCurrentCallEvent = null;
}
if(tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION) if(tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION)
{ {
broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); broadcast(new DecoderStateEvent(this, Event.END, State.FADE));
if(mCurrentCallEvent != null)
{
mCurrentCallEvent.end();
mCurrentCallEvent = null;
}
} }
else else
{ {
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); broadcast(new DecoderStateEvent(this, Event.DECODE, State.ACTIVE));
} }
switch(tdulc.getOpcode()) switch(tdulc.getOpcode())
@@ -495,22 +502,17 @@ public class P25DecoderState extends DecoderState
/* This opcode as handled at the beginning of the method */ /* This opcode as handled at the beginning of the method */
break; break;
case CHANNEL_IDENTIFIER_UPDATE: case CHANNEL_IDENTIFIER_UPDATE:
//TODO: does the activity summary need this message?
/* This message is handled by the P25MessageProcessor and /* This message is handled by the P25MessageProcessor and
* inserted into any channels needing frequency band info */ * inserted into any channels needing frequency band info */
break; break;
case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT:
//TODO: does the activity summary need this message?
/* This message is handled by the P25MessageProcessor and /* This message is handled by the P25MessageProcessor and
* inserted into any channels needing frequency band info */ * inserted into any channels needing frequency band info */
break; break;
case EXTENDED_FUNCTION_COMMAND: case EXTENDED_FUNCTION_COMMAND:
if(tdulc instanceof ExtendedFunctionCommand) if(tdulc instanceof ExtendedFunctionCommand)
{ {
ExtendedFunctionCommand efc = ExtendedFunctionCommand efc = (ExtendedFunctionCommand)tdulc;
(ExtendedFunctionCommand)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -606,42 +608,25 @@ public class P25DecoderState extends DecoderState
} }
break; break;
case GROUP_VOICE_CHANNEL_USER: case GROUP_VOICE_CHANNEL_USER:
/* Used on a traffic channel to reflect current call entities */ //Used on a traffic channel to reflect that the channel is currently being used by the
//TO talkgroup, but that channel is currently quiet (ie no voice) */
if(tdulc instanceof GroupVoiceChannelUser) if(tdulc instanceof GroupVoiceChannelUser)
{ {
GroupVoiceChannelUser gvcuser = GroupVoiceChannelUser gvcuser = (GroupVoiceChannelUser)tdulc;
(GroupVoiceChannelUser)tdulc;
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE));
//The from value should always be "000000" indicating that nobody is currently talking
mFromTalkgroupMonitor.reset();
String from = gvcuser.getSourceAddress();
String to = gvcuser.getGroupAddress(); String to = gvcuser.getGroupAddress();
mFromTalkgroupMonitor.process(from);
mToTalkgroupMonitor.process(to); mToTalkgroupMonitor.process(to);
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.GROUP_CALL)
.aliasList(getAliasList())
.channel(mCurrentChannel)
.details("TDULC GROUP VOICE CHANNEL USER" +
(gvcuser.isEncrypted() ? "ENCRYPTED " : "") +
(gvcuser.isEmergency() ? "EMERGENCY " : ""))
.frequency(mCurrentChannelFrequency)
.from(from)
.to(to)
.build();
broadcast(mCurrentCallEvent);
}
} }
break; break;
case MESSAGE_UPDATE: case MESSAGE_UPDATE:
if(tdulc instanceof MessageUpdate) if(tdulc instanceof MessageUpdate)
{ {
MessageUpdate mu = MessageUpdate mu = (MessageUpdate)tdulc;
(MessageUpdate)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.SDM)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -748,8 +733,7 @@ public class P25DecoderState extends DecoderState
io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast sccb = io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast sccb =
(io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast)tdulc; (io.github.dsheirer.module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast)tdulc;
String site = sccb.getRFSubsystemID() + "-" + String site = sccb.getRFSubsystemID() + "-" + sccb.getSiteID();
sccb.getSiteID();
mSiteAttributeMonitor.process(site); mSiteAttributeMonitor.process(site);
} }
@@ -761,11 +745,9 @@ public class P25DecoderState extends DecoderState
case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT:
if(tdulc instanceof SecondaryControlChannelBroadcastExplicit) if(tdulc instanceof SecondaryControlChannelBroadcastExplicit)
{ {
SecondaryControlChannelBroadcastExplicit sccb = SecondaryControlChannelBroadcastExplicit sccb = (SecondaryControlChannelBroadcastExplicit)tdulc;
(SecondaryControlChannelBroadcastExplicit)tdulc;
String site = sccb.getRFSubsystemID() + "-" + String site = sccb.getRFSubsystemID() + "-" + sccb.getSiteID();
sccb.getSiteID();
mSiteAttributeMonitor.process(site); mSiteAttributeMonitor.process(site);
} }
@@ -777,8 +759,7 @@ public class P25DecoderState extends DecoderState
case STATUS_QUERY: case STATUS_QUERY:
if(tdulc instanceof StatusQuery) if(tdulc instanceof StatusQuery)
{ {
StatusQuery sq = StatusQuery sq = (StatusQuery)tdulc;
(StatusQuery)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.QUERY)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -816,8 +797,7 @@ public class P25DecoderState extends DecoderState
case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: case TELEPHONE_INTERCONNECT_ANSWER_REQUEST:
if(tdulc instanceof TelephoneInterconnectAnswerRequest) if(tdulc instanceof TelephoneInterconnectAnswerRequest)
{ {
TelephoneInterconnectAnswerRequest tiar = TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)tdulc;
(TelephoneInterconnectAnswerRequest)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -835,8 +815,7 @@ public class P25DecoderState extends DecoderState
if(mChannelType == ChannelType.STANDARD && if(mChannelType == ChannelType.STANDARD &&
tdulc instanceof TelephoneInterconnectVoiceChannelUser) tdulc instanceof TelephoneInterconnectVoiceChannelUser)
{ {
TelephoneInterconnectVoiceChannelUser tivcu = TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)tdulc;
(TelephoneInterconnectVoiceChannelUser)tdulc;
String to = tivcu.getAddress(); String to = tivcu.getAddress();
@@ -860,8 +839,7 @@ public class P25DecoderState extends DecoderState
case UNIT_AUTHENTICATION_COMMAND: case UNIT_AUTHENTICATION_COMMAND:
if(tdulc instanceof UnitAuthenticationCommand) if(tdulc instanceof UnitAuthenticationCommand)
{ {
UnitAuthenticationCommand uac = UnitAuthenticationCommand uac = (UnitAuthenticationCommand)tdulc;
(UnitAuthenticationCommand)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -877,8 +855,7 @@ public class P25DecoderState extends DecoderState
case UNIT_REGISTRATION_COMMAND: case UNIT_REGISTRATION_COMMAND:
if(tdulc instanceof UnitRegistrationCommand) if(tdulc instanceof UnitRegistrationCommand)
{ {
UnitRegistrationCommand urc = UnitRegistrationCommand urc = (UnitRegistrationCommand)tdulc;
(UnitRegistrationCommand)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.COMMAND)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -894,8 +871,7 @@ public class P25DecoderState extends DecoderState
case UNIT_TO_UNIT_ANSWER_REQUEST: case UNIT_TO_UNIT_ANSWER_REQUEST:
if(tdulc instanceof UnitToUnitAnswerRequest) if(tdulc instanceof UnitToUnitAnswerRequest)
{ {
UnitToUnitAnswerRequest uuar = UnitToUnitAnswerRequest uuar = (UnitToUnitAnswerRequest)tdulc;
(UnitToUnitAnswerRequest)tdulc;
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PAGE)
.aliasList(getAliasList()) .aliasList(getAliasList())
@@ -913,27 +889,12 @@ public class P25DecoderState extends DecoderState
/* Used on traffic channels to indicate the current call entities */ /* Used on traffic channels to indicate the current call entities */
if(tdulc instanceof UnitToUnitVoiceChannelUser) if(tdulc instanceof UnitToUnitVoiceChannelUser)
{ {
UnitToUnitVoiceChannelUser uuvcu = UnitToUnitVoiceChannelUser uuvcu = (UnitToUnitVoiceChannelUser)tdulc;
(UnitToUnitVoiceChannelUser)tdulc;
String from = uuvcu.getSourceAddress(); mFromTalkgroupMonitor.reset();
mFromTalkgroupMonitor.process(from);
String to = uuvcu.getTargetAddress(); String to = uuvcu.getTargetAddress();
mToTalkgroupMonitor.process(to); mToTalkgroupMonitor.process(to);
if(mCurrentCallEvent != null)
{
mCurrentCallEvent = new P25CallEvent.Builder(CallEvent.CallEventType.UNIT_TO_UNIT_CALL)
.aliasList(getAliasList())
.channel(mCurrentChannel)
.details((uuvcu.isEncrypted() ? "ENCRYPTED " : "") +
(uuvcu.isEmergency() ? "EMERGENCY " : ""))
.frequency(mCurrentChannelFrequency)
.from(from)
.to(to)
.build();
}
} }
break; break;
default: default:
@@ -1166,9 +1127,17 @@ public class P25DecoderState extends DecoderState
(io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; (io.github.dsheirer.module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu;
mFromTalkgroupMonitor.process(gvcuser.getSourceAddress()); mFromTalkgroupMonitor.process(gvcuser.getSourceAddress());
mCurrentCallEvent.setFromID(mFromTalkgroupMonitor.getValue());
mToTalkgroupMonitor.process(gvcuser.getGroupAddress()); mToTalkgroupMonitor.process(gvcuser.getGroupAddress());
String to = mToTalkgroupMonitor.getValue();
if(mCurrentCallEvent.getToID() == null || !mCurrentCallEvent.getToID().contentEquals(to))
{
mCurrentCallEvent.setToID(to);
}
if(mChannelType == ChannelType.STANDARD) if(mChannelType == ChannelType.STANDARD)
{ {
if(mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.GROUP_CALL) if(mCurrentCallEvent.getCallEventType() != CallEvent.CallEventType.GROUP_CALL)
@@ -1631,14 +1600,13 @@ public class P25DecoderState extends DecoderState
*/ */
private void processPDU(PDUMessage pdu) private void processPDU(PDUMessage pdu)
{ {
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
if(pdu instanceof PacketData || pdu instanceof PDUTypeUnknown) if(pdu instanceof PacketData || pdu instanceof PDUTypeUnknown)
{ {
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
} }
else if(pdu instanceof PDUConfirmedMessage) else if(pdu instanceof PDUConfirmedMessage)
{ {
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu;
switch(pduc.getPDUType()) switch(pduc.getPDUType())
@@ -1723,6 +1691,11 @@ public class P25DecoderState extends DecoderState
sbTo.append(sud.getUDPDestinationPort()); sbTo.append(sud.getUDPDestinationPort());
} }
mFromTalkgroupMonitor.reset();
mFromTalkgroupMonitor.process(sbFrom.toString());
mToTalkgroupMonitor.reset();
mToTalkgroupMonitor.process(pduc.getLogicalLinkID());
broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL) broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.DATA_CALL)
@@ -1750,9 +1723,6 @@ public class P25DecoderState extends DecoderState
} }
else else
{ {
/* These are alternate trunking control blocks in PDU format */
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
switch(pdu.getOpcode()) switch(pdu.getOpcode())
{ {
case GROUP_DATA_CHANNEL_GRANT: case GROUP_DATA_CHANNEL_GRANT: