Updated Call Event handling/display for all decoders and updated channel display values for all decoders.

- Call events now show start-stop timestamps.  (or START-In Progress if call is ongoing )
 - Call start/stop events are now on the same event line.
 - Channel panels now display site and/or channel info
 - LTR call events are limited to CALLs on the current LCN, in order to cut down on false call events resulting from noise.
 - Passport display channels now show site/channel.
 - Changed default window used by the DFTProcessor to Cosine, which doesn't have as much FFT bin leakage.
This commit is contained in:
dennis.sheirer
2014-09-15 00:42:20 +00:00
parent 0f43c7ed22
commit ad456d81a3
18 changed files with 949 additions and 412 deletions

View File

@@ -26,7 +26,8 @@ import decode.DecoderType;
public abstract class CallEvent
{
protected long mEventTime = System.currentTimeMillis();
protected long mEventStart = System.currentTimeMillis();
protected long mEventEnd;
private SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd','HHmmss" );
@@ -52,6 +53,11 @@ public abstract class CallEvent
mDetails = details;
}
public void setEnd( long end )
{
mEventEnd = end;
}
public DecoderType getDecoderType()
{
return mDecoderType;
@@ -77,9 +83,14 @@ public abstract class CallEvent
return mAliasList != null;
}
public long getEventTime()
public long getEventStartTime()
{
return mEventTime;
return mEventStart;
}
public long getEventEndTime()
{
return mEventEnd;
}
public String getFromID()
@@ -122,7 +133,8 @@ public abstract class CallEvent
public static String getCSVHeader()
{
return "DATE,TIME,DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,"
return "START_DATE,START_TIME,END_DATE,END_TIME,"
+ "DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,"
+ "CHANNEL,FREQUENCY,DETAILS";
}
@@ -131,7 +143,16 @@ public abstract class CallEvent
StringBuilder sb = new StringBuilder();
sb.append( "'" );
sb.append( mSDF.format( new Date( getEventTime() ) ) );
sb.append( mSDF.format( new Date( getEventStartTime() ) ) );
sb.append( "','" );
if( mEventEnd != 0 )
{
sb.append( mSDF.format( new Date( getEventEndTime() ) ) );
}
else
{
sb.append( "','" );
}
sb.append( "','" );
sb.append( getDecoderType().toString() );
sb.append( "','" );
@@ -189,11 +210,11 @@ public abstract class CallEvent
public enum CallEventType
{
ACKNOWLEDGE( "Acknowledge" ),
CALL_DETECT( "Call Detect" ),
CALL_START( "Call Start" ),
CALL_START_UNIQUE_ID( "Call Start" ),
CALL_START_NO_TUNER( "Call - No Tuner" ),
CALL( "Call" ),
CALL_END( "Call End" ),
CALL_DETECT( "Call Detect" ),
CALL_UNIQUE_ID( "UID Call" ),
CALL_NO_TUNER( "Call - No Tuner" ),
CALL_TIMEOUT( "Call Timeout" ),
EMERGENCY( "EMERGENCY" ),
GPS( "GPS" ),
@@ -205,7 +226,8 @@ public abstract class CallEvent
REQUEST( "Request" ),
SDM( "SDM" ),
STATUS( "Status" ),
UNKNOWN( "Unknown" );
UNKNOWN( "Unknown" ),
INVALID( "Invalid" );
private String mLabel;

View File

@@ -18,29 +18,36 @@
package controller.activity;
import java.awt.EventQueue;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.LinkedList;
import javax.swing.table.AbstractTableModel;
import log.Log;
import sample.Broadcaster;
import sample.Listener;
import controller.activity.CallEvent.CallEventType;
public class CallEventModel extends AbstractTableModel
{
private static final long serialVersionUID = 1L;
private static DecimalFormat mFrequencyFormatter =
new DecimalFormat( "0.000000" );
private static final int DEFAULT_ICON_HEIGHT = 14;
private static final int sTIME = 0;
private static final int sEVENT = 1;
private static final int sFROM_ID = 2;
private static final int sFROM_ALIAS = 3;
private static final int sTO_ID = 4;
private static final int sTO_ALIAS =5;
private static final int sCHANNEL = 6;
private static final int sFREQUENCY = 7;
private static final int sDETAILS = 8;
public static final int TIME = 0;
public static final int EVENT = 1;
public static final int FROM_ID = 2;
public static final int FROM_ALIAS = 3;
public static final int TO_ID = 4;
public static final int TO_ALIAS = 5;
public static final int CHANNEL = 6;
public static final int FREQUENCY = 7;
public static final int DETAILS = 8;
protected int mMaxMessages = 500;
@@ -49,9 +56,9 @@ public class CallEventModel extends AbstractTableModel
protected String[] mHeaders = new String[] { "Time",
"Event",
"From",
"From Alias",
"Alias",
"To",
"To Alias",
"Alias",
"Channel",
"Frequency",
"Details" };
@@ -67,16 +74,23 @@ public class CallEventModel extends AbstractTableModel
public void dispose()
{
mEvents.clear();
synchronized( mEvents )
{
mEvents.clear();
}
mBroadcaster.dispose();
}
public void flush()
{
/* Flush all events to listeners ( normally the call event logger ) */
for( CallEvent event: mEvents )
synchronized( mEvents )
{
mBroadcaster.receive( event );
for( CallEvent event: mEvents )
{
mBroadcaster.receive( event );
}
}
}
@@ -91,30 +105,137 @@ public class CallEventModel extends AbstractTableModel
}
public void add( final CallEvent event )
{
try
{
synchronized ( mEvents )
{
mEvents.addFirst( event );
}
fireTableRowsInserted( 0, 0 );
prune();
}
catch( Exception e )
{
e.printStackTrace();
}
}
public void remove( final CallEvent event )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
mEvents.addFirst( event );
CallEventModel.this.fireTableRowsInserted( 0, 0 );
prune();
synchronized ( mEvents )
{
final int row = mEvents.indexOf( event );
mEvents.remove( event );
if( row != -1 )
{
fireTableRowsDeleted( row, row );
}
}
}
} );
}
public int indexOf( CallEvent event )
{
return mEvents.indexOf( event );
}
public void setEnd( final CallEvent event )
{
try
{
EventQueue.invokeAndWait( new Runnable()
{
@Override
public void run()
{
if( event != null )
{
event.setEnd( System.currentTimeMillis() );
synchronized ( mEvents )
{
int row = indexOf( event );
if( row != -1 )
{
fireTableCellUpdated( row, TIME );
}
}
}
else
{
Log.error( "CallEventModel - couldn't log end time - event was null" );
}
}
} );
}
catch( Exception e )
{
e.printStackTrace();
}
}
public void setFromID( final CallEvent event, final String from )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
try
{
if( event != null )
{
event.setFromID( from );
synchronized ( mEvents )
{
int row = indexOf( event );
if( row != -1 )
{
fireTableCellUpdated( row, FROM_ID );
fireTableCellUpdated( row, FROM_ALIAS );
}
else
{
Log.error( "CallEventModel - tried to set from ID on "
+ "call event - couldn't find index of event" );
}
}
}
}
catch( Exception e )
{
e.printStackTrace();
}
}
} );
}
private void prune()
{
while( mEvents.size() > mMaxMessages )
synchronized( mEvents )
{
CallEvent event = mEvents.removeLast();
mBroadcaster.receive( event );
super.fireTableRowsDeleted( mEvents.size(), mEvents.size() );
while( mEvents.size() > mMaxMessages )
{
CallEvent event = mEvents.removeLast();
mBroadcaster.receive( event );
fireTableRowsDeleted( mEvents.size(), mEvents.size() );
}
}
}
@@ -138,26 +259,64 @@ public class CallEventModel extends AbstractTableModel
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
switch( columnIndex )
synchronized( mEvents )
{
case sTIME:
return mSDFTime.format( mEvents.get( rowIndex ).getEventTime() );
case sEVENT:
return mEvents.get( rowIndex ).getCallEventType();
case sFROM_ID:
return mEvents.get( rowIndex ).getFromID();
case sFROM_ALIAS:
return mEvents.get( rowIndex ).getFromIDAlias();
case sTO_ID:
return mEvents.get( rowIndex ).getToID();
case sTO_ALIAS:
return mEvents.get( rowIndex ).getToIDAlias();
case sCHANNEL:
return mEvents.get( rowIndex ).getChannel();
case sFREQUENCY:
return mEvents.get( rowIndex ).getFrequency();
case sDETAILS:
return mEvents.get( rowIndex ).getDetails();
switch( columnIndex )
{
case TIME:
StringBuilder sb = new StringBuilder();
sb.append( mSDFTime.format(
mEvents.get( rowIndex ).getEventStartTime() ) );
if( mEvents.get( rowIndex ).getEventEndTime() != 0 )
{
sb.append( " - " );
sb.append( mSDFTime.format(
mEvents.get( rowIndex ).getEventEndTime() ) );
}
else if( mEvents.get( rowIndex )
.getCallEventType() == CallEventType.CALL )
{
sb.append( " - In Progress" );
}
return sb.toString();
case EVENT:
return mEvents.get( rowIndex ).getCallEventType();
case FROM_ID:
return mEvents.get( rowIndex ).getFromID();
case FROM_ALIAS:
return mEvents.get( rowIndex ).getFromIDAlias();
case TO_ID:
return mEvents.get( rowIndex ).getToID();
case TO_ALIAS:
return mEvents.get( rowIndex ).getToIDAlias();
case CHANNEL:
int channel = mEvents.get( rowIndex ).getChannel();
if( channel != 0 )
{
return channel;
}
else
{
return null;
}
case FREQUENCY:
long frequency = mEvents.get( rowIndex ).getFrequency();
if( frequency != 0 )
{
return mFrequencyFormatter.format( (double)frequency / 1E6d );
}
else
{
return null;
}
case DETAILS:
return mEvents.get( rowIndex ).getDetails();
}
}
return null;

View File

@@ -17,6 +17,7 @@
******************************************************************************/
package controller.state;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ScheduledFuture;
@@ -42,6 +43,7 @@ import controller.activity.CallEventModel;
import controller.activity.MessageActivityModel;
import controller.channel.Channel.ChannelType;
import controller.channel.ProcessingChain;
import decode.mpt1327.MPT1327CallEvent;
/**
* ChannelState provides a state machine for tracking a voice or data call
@@ -98,7 +100,6 @@ public abstract class ChannelState implements Listener<Message>
protected AudioTypeListener mAudioTypeListener;
protected AudioType mAudioType = AudioType.NORMAL;
protected CallEventModel mCallEventModel = new CallEventModel();
protected JTable mCallEventTable;
@@ -111,6 +112,8 @@ public abstract class ChannelState implements Listener<Message>
protected AliasList mAliasList;
protected CallEvent mCurrentCallEvent;
public ChannelState( ProcessingChain processingChain, AliasList aliasList )
{
mProcessingChain = processingChain;
@@ -125,6 +128,16 @@ public abstract class ChannelState implements Listener<Message>
mCallResetTimeout = props.get( "call.reset.timeout", 4000 );
}
public void setCurrentCallEvent( CallEvent callEvent )
{
mCurrentCallEvent = callEvent;
}
public CallEvent getCurrentCallEvent()
{
return mCurrentCallEvent;
}
/**
* Allows setting the call event model to use for traffic channels, so that
* all traffic channels can share the parent control channel call event
@@ -190,12 +203,9 @@ public abstract class ChannelState implements Listener<Message>
return mAliasList != null;
}
public void broadcastChange( ChangedAttribute attribute )
public void broadcastChange( final ChangedAttribute attribute )
{
if( mChangeBroadcaster != null )
{
mChangeBroadcaster.broadcast( attribute );
}
mChangeBroadcaster.broadcast( attribute );
}
public void setCallFadeTimeout( long milliseconds )
@@ -648,6 +658,7 @@ public abstract class ChannelState implements Listener<Message>
CHANNEL_SITE_NUMBER,
CHANNEL_SITE_NUMBER_ALIAS,
CHANNEL_STATE,
DESCRIPTION,
FROM_TALKGROUP,
FROM_TALKGROUP_ALIAS,
FROM_TALKGROUP_TYPE,