diff --git a/src/controller/activity/CallEvent.java b/src/controller/activity/CallEvent.java index ebf6de87..e84722f2 100644 --- a/src/controller/activity/CallEvent.java +++ b/src/controller/activity/CallEvent.java @@ -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; diff --git a/src/controller/activity/CallEventModel.java b/src/controller/activity/CallEventModel.java index f94058cb..7a43f609 100644 --- a/src/controller/activity/CallEventModel.java +++ b/src/controller/activity/CallEventModel.java @@ -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; diff --git a/src/controller/state/ChannelState.java b/src/controller/state/ChannelState.java index 0c87c0a2..9d116289 100644 --- a/src/controller/state/ChannelState.java +++ b/src/controller/state/ChannelState.java @@ -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 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 protected AliasList mAliasList; + protected CallEvent mCurrentCallEvent; + public ChannelState( ProcessingChain processingChain, AliasList aliasList ) { mProcessingChain = processingChain; @@ -125,6 +128,16 @@ public abstract class ChannelState implements Listener 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 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 CHANNEL_SITE_NUMBER, CHANNEL_SITE_NUMBER_ALIAS, CHANNEL_STATE, + DESCRIPTION, FROM_TALKGROUP, FROM_TALKGROUP_ALIAS, FROM_TALKGROUP_TYPE, diff --git a/src/decode/fleetsync2/FleetsyncChannelState.java b/src/decode/fleetsync2/FleetsyncChannelState.java index cc2c3194..32052d3e 100644 --- a/src/decode/fleetsync2/FleetsyncChannelState.java +++ b/src/decode/fleetsync2/FleetsyncChannelState.java @@ -17,6 +17,7 @@ ******************************************************************************/ package decode.fleetsync2; +import log.Log; import message.Message; import alias.Alias; import controller.state.AuxChannelState; diff --git a/src/decode/ltrnet/LTRCallEvent.java b/src/decode/ltrnet/LTRCallEvent.java index b1ba797b..d0690d4c 100644 --- a/src/decode/ltrnet/LTRCallEvent.java +++ b/src/decode/ltrnet/LTRCallEvent.java @@ -21,11 +21,13 @@ import alias.Alias; import alias.AliasList; import controller.activity.CallEvent; import decode.DecoderType; +import decode.ltrstandard.LTRStandardMessage; public class LTRCallEvent extends CallEvent { private int mChannel; private long mFrequency; + private int mValidCallMessages = 1; public LTRCallEvent( CallEventType callEventType, AliasList aliasList, @@ -41,6 +43,50 @@ public class LTRCallEvent extends CallEvent mChannel = channel; mFrequency = frequency; } + + /** + * LTR sends multiple CALL messages during a call. In order to detect noise + * bursts from valid calls, send every valid call message to this method + * and the event will keep a tally of valid call messages in order to + * determine if the call was a valid (and loggable) call event. + */ + public boolean addMessage( LTRStandardMessage message ) + { + String talkgroup = message.getTalkgroupID(); + + if( talkgroup.contentEquals( getToID() ) ) + { + mValidCallMessages++; + + return true; + } + + return false; + } + + public boolean addMessage( LTRNetMessage message ) + { + String talkgroup = message.getTalkgroupID(); + + if( talkgroup.contentEquals( getToID() ) ) + { + mValidCallMessages++; + + return true; + } + + return false; + } + + public boolean isValid() + { + if( getCallEventType() == CallEventType.CALL ) + { + return mValidCallMessages > 1; + } + + return true; + } private Alias getAlias( String talkgroup ) { @@ -55,7 +101,7 @@ public class LTRCallEvent extends CallEvent @Override public Alias getFromIDAlias() { - if( mCallEventType == CallEventType.CALL_START || + if( mCallEventType == CallEventType.CALL || mCallEventType == CallEventType.REGISTER || mCallEventType == CallEventType.ID_UNIQUE ) { diff --git a/src/decode/ltrnet/LTRNetChannelState.java b/src/decode/ltrnet/LTRNetChannelState.java index b5b46a51..6674c173 100644 --- a/src/decode/ltrnet/LTRNetChannelState.java +++ b/src/decode/ltrnet/LTRNetChannelState.java @@ -17,23 +17,27 @@ ******************************************************************************/ package decode.ltrnet; +import java.awt.EventQueue; +import java.util.HashMap; + import message.Message; import alias.Alias; import alias.AliasList; +import controller.activity.CallEvent; import controller.activity.CallEvent.CallEventType; +import controller.activity.CallEventModel; import controller.channel.ProcessingChain; import controller.state.AuxChannelState; import controller.state.ChannelState; +import controller.state.ChannelState.State; public class LTRNetChannelState extends ChannelState { - private String mToTalkgroup; - private Alias mToTalkgroupAlias; - private String mFromTalkgroup; - private Alias mFromTalkgroupAlias; - private String mFromTalkgroupType; + private String mTalkgroup; + private Alias mTalkgroupAlias; + private String mDescription; private int mChannelNumber; - private LTRCallEvent mCurrentCallEvent; + private HashMap mActiveCalls = new HashMap(); private LTRNetActivitySummary mActivitySummary; public LTRNetChannelState( ProcessingChain channel, AliasList aliasList ) @@ -50,27 +54,47 @@ public class LTRNetChannelState extends ChannelState public void fade( final CallEventType type ) { /* - * We can receive multiple call tear-down messages -- only post a call - * end event for the one that can change the state to fade + * We can receive multiple call tear-down messages -- only respond to + * the message that can change the state to fade */ if( getState().canChangeTo( State.FADE ) ) { - mCallEventModel.add( - new LTRCallEvent.Builder( type ) - .aliasList( getAliasList() ) - .channel( mChannelNumber ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( mToTalkgroup ) - .build() ); - + LTRCallEvent current = getCurrentLTRCallEvent(); + + if( current != null ) + { + /* Close out the call event. If we only received 1 call + * message, then flag it as noise and remove the call event */ + if( current.isValid() ) + { + mCallEventModel.setEnd( current ); + } + else + { + mCallEventModel.remove( current ); + } + } } - mCurrentCallEvent = null; + setCurrentCallEvent( null ); + + mActiveCalls.clear(); super.fade( type ); } + public LTRCallEvent getCurrentLTRCallEvent() + { + CallEvent current = getCurrentCallEvent(); + + if( current != null ) + { + return (LTRCallEvent)current; + } + + return null; + } + @Override public String getActivitySummary() { @@ -103,64 +127,106 @@ public class LTRNetChannelState extends ChannelState switch( ltr.getMessageType() ) { case CA_STRT: - mToTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) + if( mChannelNumber == 0 ) { - mToTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); + mChannelNumber = ltr.getChannel(); } - mChannelNumber = ltr.getChannel(); - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - - if( getState() != ChannelState.State.CALL ) + if( ltr.getChannel() == mChannelNumber ) { - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.CALL_START ) + processCallMessage( ltr ); + + if( ltr.getGroup() == 253 ) + { + mDescription = "REGISTER"; + broadcastChange( ChangedAttribute.DESCRIPTION ); + } + else + { + mTalkgroup = ltr.getTalkgroupID(); + broadcastChange( ChangedAttribute.TO_TALKGROUP ); + + if( talkgroupAlias != null ) + { + mTalkgroupAlias = talkgroupAlias; + broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + } + } + else + { + //Call Detect + if( !mActiveCalls.containsKey( ltr.getChannel() ) || + !mActiveCalls.get( ltr.getChannel() ) + .contentEquals( ltr.getTalkgroupID() ) ) + { + mActiveCalls.put( ltr.getChannel(), + ltr.getTalkgroupID() ); + + mCallEventModel.add( + new LTRCallEvent.Builder( CallEventType.CALL_DETECT ) .aliasList( mAliasList ) .channel( ltr.getChannel() ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( mToTalkgroup ) + .to( ltr.getTalkgroupID() ) .build() ); + } } - - setState( State.CALL ); break; case CA_ENDD: - mToTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) + if( ltr.getGroup() != 253 ) { - mToTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); + mTalkgroup = ltr.getTalkgroupID(); + broadcastChange( ChangedAttribute.TO_TALKGROUP ); + + if( talkgroupAlias != null ) + { + mTalkgroupAlias = talkgroupAlias; + broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } } fade( CallEventType.CALL_END ); break; case ID_UNIQ: - mFromTalkgroupType = "UID"; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE ); + mDescription = "REGISTER UID"; + broadcastChange( ChangedAttribute.DESCRIPTION ); - mFromTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); + mTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); + broadcastChange( ChangedAttribute.TO_TALKGROUP ); - mFromTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); + broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - mCallEventModel.add( + final LTRCallEvent currentEvent = getCurrentLTRCallEvent(); + + if( currentEvent != null && + currentEvent.getCallEventType() == CallEventType.REGISTER ) + { + final String uid = String.valueOf( ltr.getRadioUniqueID() ); + + if( uid != null ) + { + mCallEventModel.setFromID( currentEvent, uid ); + } + } + else + { + mCallEventModel.add( new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "Radio Unique ID" ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .from( String.valueOf( ltr.getRadioUniqueID() ) ) - .build() ); + .aliasList( mAliasList ) + .details( "Radio Unique ID" ) + .frequency( mProcessingChain.getChannel() + .getTunerChannel().getFrequency() ) + .from( String.valueOf( ltr.getRadioUniqueID() ) ) + .build() ); + } setState( State.CALL ); + break; + case SY_IDLE: + mChannelNumber = ltr.getChannel(); + broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); + break; default: break; } @@ -177,85 +243,83 @@ public class LTRNetChannelState extends ChannelState switch( ltr.getMessageType() ) { case CA_STRT: - mToTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mToTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } + processCallMessage( ltr ); - if( getState() != State.CALL ) + if( ltr.getGroup() == 253 ) { - mCurrentCallEvent = - new LTRCallEvent.Builder( CallEventType.CALL_START ) - .aliasList( mAliasList ) - .channel( ltr.getChannel() ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( mToTalkgroup ) - .build(); - - mCallEventModel.add( mCurrentCallEvent ); + mDescription = "REGISTER"; + broadcastChange( ChangedAttribute.DESCRIPTION ); + } + else + { + mTalkgroup = ltr.getTalkgroupID(); + broadcastChange( ChangedAttribute.TO_TALKGROUP ); + + if( talkgroupAlias != null ) + { + mTalkgroupAlias = talkgroupAlias; + broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } } - setState( State.CALL ); break; case CA_ENDD: - mToTalkgroup = ltr.getTalkgroupID(); + mTalkgroup = ltr.getTalkgroupID(); broadcastChange( ChangedAttribute.TO_TALKGROUP ); if( talkgroupAlias != null ) { - mToTalkgroupAlias = talkgroupAlias; + mTalkgroupAlias = talkgroupAlias; broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); } fade( CallEventType.CALL_END ); break; case ID_UNIQ: - mFromTalkgroupType = "UID"; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE ); + mDescription = "REGISTER UID"; + broadcastChange( ChangedAttribute.DESCRIPTION ); - mFromTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); + mTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); + broadcastChange( ChangedAttribute.TO_TALKGROUP ); - mFromTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); + broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - if( getState() != State.CALL ) + if( getState() != State.DATA ) { - mCallEventModel.add( new LTRCallEvent.Builder( - CallEventType.ID_UNIQUE ) - .aliasList( mAliasList ) - .details( "Unique ID" ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .from( String.valueOf( ltr.getRadioUniqueID() ) ) - .build() ); + LTRCallEvent event = + new LTRCallEvent.Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .channel( ltr.getChannel() ) + .frequency( mProcessingChain.getChannel() + .getTunerChannel().getFrequency() ) + .to( ltr.getTalkgroupID() ) + .build(); + + mCallEventModel.add( event ); + + setCurrentCallEvent( event ); + + setState( State.DATA ); } - else + + if( mCurrentCallEvent != null ) { - if( mCurrentCallEvent != null ) - { - mCurrentCallEvent.setFromID( - String.valueOf( ltr.getRadioUniqueID() ) ); - mCurrentCallEvent.setDetails( "Unique ID" ); - } + mCurrentCallEvent.setFromID( + String.valueOf( ltr.getRadioUniqueID() ) ); + mCurrentCallEvent.setDetails( "Unique ID" ); } - setState( State.CALL ); break; case ID_ESNH: case ID_ESNL: - mFromTalkgroupType = "ESN"; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE ); + mDescription = "ESN"; + broadcastChange( ChangedAttribute.DESCRIPTION ); - mFromTalkgroup = ltr.getESN(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); + mTalkgroup = ltr.getESN(); + broadcastChange( ChangedAttribute.TO_TALKGROUP ); - mFromTalkgroupAlias = ltr.getESNAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + mTalkgroupAlias = ltr.getESNAlias(); + broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - setState( State.CALL ); + setState( State.DATA ); mCallEventModel.add( new LTRCallEvent.Builder( CallEventType.REGISTER_ESN ) @@ -273,49 +337,88 @@ public class LTRNetChannelState extends ChannelState } } + private void processCallMessage( LTRNetMessage message ) + { + LTRCallEvent current = getCurrentLTRCallEvent(); + + if( current == null || !current.addMessage( message ) ) + { + if( current != null ) + { + mCallEventModel.remove( current ); + } + + LTRCallEvent event; + + if( message.getGroup() == 253 ) + { + event = new LTRCallEvent.Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .channel( message.getChannel() ) + .frequency( mProcessingChain.getChannel() + .getTunerChannel().getFrequency() ) + .to( message.getTalkgroupID() ) + .build(); + + setState( State.DATA ); + } + else + { + event = new LTRCallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( message.getChannel() ) + .frequency( mProcessingChain.getChannel() + .getTunerChannel().getFrequency() ) + .to( message.getTalkgroupID() ) + .build(); + + setState( State.CALL ); + } + + mCallEventModel.add( event ); + + setCurrentCallEvent( event ); + } + else + { + if( getState() == State.CALL ) + { + setState( State.CALL ); + } + else if( getState() == State.DATA ) + { + setState( State.DATA ); + } + } + } + public void reset() { - mToTalkgroup = null; + mTalkgroup = null; broadcastChange( ChangedAttribute.TO_TALKGROUP ); - mToTalkgroupAlias = null; + mTalkgroupAlias = null; broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - mFromTalkgroup = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mFromTalkgroupAlias = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - mFromTalkgroupType = " "; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE ); + mDescription = null; + broadcastChange( ChangedAttribute.DESCRIPTION ); super.reset(); } public String getToTalkgroup() { - return mToTalkgroup; + return mTalkgroup; } public Alias getToTalkgroupAlias() { - return mToTalkgroupAlias; + return mTalkgroupAlias; } - public String getFromTalkgroup() + public String getDescription() { - return mFromTalkgroup; - } - - public Alias getFromTalkgroupAlias() - { - return mFromTalkgroupAlias; - } - - public String getFromTalkgroupType() - { - return mFromTalkgroupType; + return mDescription; } public boolean hasChannelNumber() diff --git a/src/decode/ltrnet/LTRNetPanel.java b/src/decode/ltrnet/LTRNetPanel.java index 5d7e4a2c..276acf48 100644 --- a/src/decode/ltrnet/LTRNetPanel.java +++ b/src/decode/ltrnet/LTRNetPanel.java @@ -37,12 +37,11 @@ public class LTRNetPanel extends ChannelStatePanel private JLabel mChannelLabel; private JLabel mProtocol = new JLabel( "LTR-Net" ); - private JLabel mToTalkgroup = new JLabel(); - private JLabel mToTalkgroupAlias = new JLabel(); + private JLabel mTalkgroup = new JLabel(); + private JLabel mTalkgroupAlias = new JLabel(); - private JLabel mFromType = new JLabel( " " ); - private JLabel mFromTalkgroup = new JLabel(); - private JLabel mFromTalkgroupAlias = new JLabel(); + private JLabel mLCN = new JLabel( "LCN:" ); + private JLabel mDescription = new JLabel(); public LTRNetPanel( Channel channel ) { @@ -69,32 +68,28 @@ public class LTRNetPanel extends ChannelStatePanel mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); - mToTalkgroup.setFont( mFontDecoder ); - mToTalkgroup.setForeground( mColorLabelDecoder ); + mTalkgroup.setFont( mFontDecoder ); + mTalkgroup.setForeground( mColorLabelDecoder ); - mToTalkgroupAlias.setFont( mFontDecoder ); - mToTalkgroupAlias.setForeground( mColorLabelDecoder ); + mTalkgroupAlias.setFont( mFontDecoder ); + mTalkgroupAlias.setForeground( mColorLabelDecoder ); - mFromType.setFont( mFontDecoder ); - mFromType.setForeground( mColorLabelDecoder ); + mLCN.setFont( mFontDecoder ); + mLCN.setForeground( mColorLabelDecoder ); - mFromTalkgroup.setFont( mFontDecoder ); - mFromTalkgroup.setForeground( mColorLabelDecoder ); - - mFromTalkgroupAlias.setFont( mFontDecoder ); - mFromTalkgroupAlias.setForeground( mColorLabelDecoder ); + mDescription.setFont( mFontDecoder ); + mDescription.setForeground( mColorLabelDecoder ); add( mStateLabel ); add( mSourceLabel ); add( mChannelLabel, "wrap" ); add( mProtocol ); - add( mToTalkgroup ); - add( mToTalkgroupAlias, "wrap" ); + add( mTalkgroup ); + add( mTalkgroupAlias, "wrap" ); - add( mFromType ); - add( mFromTalkgroup ); - add( mFromTalkgroupAlias, "wrap" ); + add( mLCN ); + add( mDescription, "span,wrap" ); } @Override @@ -122,47 +117,31 @@ public class LTRNetPanel extends ChannelStatePanel case SYSTEM_NAME: mChannelLabel.setText( mChannel.getChannelDisplayName() ); break; - case FROM_TALKGROUP: - mFromTalkgroup.setText( state.getFromTalkgroup() ); - break; - case FROM_TALKGROUP_ALIAS: - Alias fromAlias = state.getFromTalkgroupAlias(); - - if( fromAlias != null ) - { - mFromTalkgroupAlias.setText( fromAlias.getName() ); - String iconNameString = fromAlias.getIconName(); - mFromTalkgroupAlias.setIcon( getSettingsManager() - .getImageIcon( iconNameString, 12 ) ); - } - else - { - mFromTalkgroupAlias.setText( null ); - mFromTalkgroupAlias.setIcon( null ); - } - break; - case FROM_TALKGROUP_TYPE: - mFromType.setText( state.getFromTalkgroupType() ); - break; case TO_TALKGROUP: - mToTalkgroup.setText( state.getToTalkgroup() ); + mTalkgroup.setText( state.getToTalkgroup() ); break; case TO_TALKGROUP_ALIAS: Alias tgAlias = state.getToTalkgroupAlias(); if( tgAlias != null ) { - mToTalkgroupAlias.setText( tgAlias.getName() ); + mTalkgroupAlias.setText( tgAlias.getName() ); String iconNameString = tgAlias.getIconName(); - mToTalkgroupAlias.setIcon( getSettingsManager() + mTalkgroupAlias.setIcon( getSettingsManager() .getImageIcon( iconNameString, 12 ) ); } else { - mToTalkgroupAlias.setText( null ); - mToTalkgroupAlias.setIcon( null ); + mTalkgroupAlias.setText( null ); + mTalkgroupAlias.setIcon( null ); } break; + case DESCRIPTION: + mDescription.setText( state.getDescription() ); + break; + case CHANNEL_NUMBER: + mLCN.setText( "LCN: " + state.getChannelNumber() ); + break; default: break; } @@ -192,25 +171,21 @@ public class LTRNetPanel extends ChannelStatePanel { mProtocol.setForeground( mColorLabelDecoder ); } - if( mToTalkgroup != null ) + if( mTalkgroup != null ) { - mToTalkgroup.setForeground( mColorLabelDecoder ); + mTalkgroup.setForeground( mColorLabelDecoder ); } - if( mToTalkgroupAlias != null ) + if( mTalkgroupAlias != null ) { - mToTalkgroupAlias.setForeground( mColorLabelDecoder ); + mTalkgroupAlias.setForeground( mColorLabelDecoder ); } - if( mFromType != null ) + if( mLCN != null ) { - mFromType.setForeground( mColorLabelDecoder ); + mLCN.setForeground( mColorLabelDecoder ); } - if( mFromTalkgroup != null ) + if( mDescription != null ) { - mFromTalkgroup.setForeground( mColorLabelDecoder ); - } - if( mFromTalkgroupAlias != null ) - { - mFromTalkgroupAlias.setForeground( mColorLabelDecoder ); + mDescription.setForeground( mColorLabelDecoder ); } break; case CHANNEL_STATE_LABEL_DETAILS: diff --git a/src/decode/ltrstandard/LTRChannelState.java b/src/decode/ltrstandard/LTRChannelState.java index 12b2077e..462588eb 100644 --- a/src/decode/ltrstandard/LTRChannelState.java +++ b/src/decode/ltrstandard/LTRChannelState.java @@ -22,7 +22,9 @@ import java.util.HashMap; import message.Message; import alias.Alias; import alias.AliasList; +import controller.activity.CallEvent; import controller.activity.CallEvent.CallEventType; +import controller.activity.CallEventModel; import controller.channel.ProcessingChain; import controller.state.AuxChannelState; import controller.state.ChannelState; @@ -72,19 +74,24 @@ public class LTRChannelState extends ChannelState switch( ltr.getMessageType() ) { + /** + * Process Call Message + * + * States: + * 1) Call message with invalid LCN + * 2) State=IDLE/FADE, new CA_STRT + * 3) State=CALL, new CA_STRT, same talkgroup + * 4) State=CALL, new CA_STRT, different talkgroup + */ case CA_STRT: if( 0 < ltr.getChannel() && ltr.getChannel() <=20 ) { - /* If this channel is the same as the talkgroup's home - * repeater, then the channel number refers to the - * currently tuned frequency and we capture the channel - * number ... otherwise, it might be reflecting a call - * on another LCN */ - if( ltr.getChannel() == ltr.getHomeRepeater() ) - { + /* If we haven't captured the LCN yet ... */ + if( mChannelNumber == 0 ) + { mChannelNumber = ltr.getChannel(); broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } + } /* If this call event is for this channel, then update * talkgroup and alias */ @@ -95,32 +102,47 @@ public class LTRChannelState extends ChannelState mTalkgroupAlias = ltr.getTalkgroupIDAlias(); broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + + LTRCallEvent current = getCurrentLTRCallEvent(); + + /* If we have an ongoing call, then add this message + * to the current event - if addMessage returns + * false, then we have a different talkgroup and a + * new call event, so close out the old one. */ + if( current != null && !current.addMessage( ltr ) ) + { + if( current.isValid() ) + { + mCallEventModel.setEnd( current ); + } + else + { + mCallEventModel.remove( current ); + } + + setCallEventModel( null ); + current = null; + } + + if( current == null ) + { + LTRCallEvent event = + new LTRCallEvent.Builder( CallEventType.CALL ) + .to( ltr.getTalkgroupID() ) + .aliasList( getAliasList() ) + .channel( ltr.getChannel() ) + .frequency( ( ltr.getChannel() == mChannelNumber ) ? + mProcessingChain.getChannel() + .getTunerChannel().getFrequency() : 0 ) + .build(); + + mCallEventModel.add( event ); + + setCurrentCallEvent( event ); + } + + setState( State.CALL ); } - - /* If this is a new call, log a call event */ - if( !mActiveCalls.containsKey( ltr.getChannel() ) || - !mActiveCalls.get( ltr.getChannel() ) - .contentEquals( ltr.getTalkgroupID() ) ) - { - mActiveCalls.put( ltr.getChannel(), ltr.getTalkgroupID() ); - - CallEventType callEventType = - ( ltr.getChannel() == mChannelNumber ) ? - CallEventType.CALL_START : - CallEventType.CALL_DETECT; - - mCallEventModel.add( - new LTRCallEvent.Builder( callEventType ) - .to( ltr.getTalkgroupID() ) - .aliasList( getAliasList() ) - .channel( ltr.getChannel() ) - .frequency( ( ltr.getChannel() == mChannelNumber ) ? - mProcessingChain.getChannel() - .getTunerChannel().getFrequency() : 0 ) - .build() ); - } - - setState( State.CALL ); } break; case CA_ENDD: @@ -149,27 +171,47 @@ public class LTRChannelState extends ChannelState } } + public LTRCallEvent getCurrentLTRCallEvent() + { + CallEvent current = getCurrentCallEvent(); + + if( current != null ) + { + return (LTRCallEvent)current; + } + + return null; + } + /** * Intercept the fade event so that we can generate a call end event */ @Override public void fade( final CallEventType type ) { - /* - * We can receive multiple call tear-down messages -- only post a call - * end event for the one that can change the state to fade - */ + /* We can receive multiple call tear-down messages -- only respond to + * the message that can change the state to fade */ if( getState().canChangeTo( State.FADE ) ) { mActiveCalls.clear(); - mCallEventModel.add( - new LTRCallEvent.Builder( type ) - .aliasList( getAliasList() ) - .channel( mChannelNumber ) - .frequency( mProcessingChain.getChannel().getTunerChannel().getFrequency() ) - .to( mTalkgroup ) - .build() ); + LTRCallEvent current = getCurrentLTRCallEvent(); + + if( current != null ) + { + /* Close out the call event. If we only received 1 call + * message, then flag it as noise and remove the call event */ + if( current.isValid() ) + { + mCallEventModel.setEnd( current ); + } + else + { + mCallEventModel.remove( current ); + } + } + + setCurrentCallEvent( null ); } super.fade( type ); diff --git a/src/decode/ltrstandard/LTRPanel.java b/src/decode/ltrstandard/LTRPanel.java index ca8790ee..921f9c4a 100644 --- a/src/decode/ltrstandard/LTRPanel.java +++ b/src/decode/ltrstandard/LTRPanel.java @@ -41,8 +41,7 @@ public class LTRPanel extends ChannelStatePanel private JLabel mTalkgroup = new JLabel(); private JLabel mTalkgroupAlias = new JLabel(); - private JLabel mLCNLabel = new JLabel( "LCN" ); - private JLabel mLCN = new JLabel(); + private JLabel mLCN = new JLabel( "LCN:" ); private SettingsManager mSettingsManager; @@ -86,9 +85,6 @@ public class LTRPanel extends ChannelStatePanel mTalkgroupAlias.setFont( mFontDecoder ); mTalkgroupAlias.setForeground( mColorLabelDecoder ); - mLCNLabel.setFont( mFontDecoder ); - mLCNLabel.setForeground( mColorLabelDetails ); - mLCN.setFont( mFontDecoder ); mLCN.setForeground( mColorLabelDecoder ); @@ -100,7 +96,6 @@ public class LTRPanel extends ChannelStatePanel add( mTalkgroup ); add( mTalkgroupAlias, "wrap" ); - add( mLCNLabel ); add( mLCN, "wrap" ); } @@ -120,12 +115,7 @@ public class LTRPanel extends ChannelStatePanel case CHANNEL_NUMBER: if( state.getChannelNumber() != 0 ) { - mLCN.setText( - String.valueOf( state.getChannelNumber() ) ); - } - else - { - mLCN.setText( "" ); + mLCN.setText( "LCN: " + state.getChannelNumber() ); } break; case FROM_TALKGROUP: @@ -208,10 +198,6 @@ public class LTRPanel extends ChannelStatePanel { mChannelLabel.setForeground( mColorLabelDetails ); } - if( mLCNLabel != null ) - { - mLCNLabel.setForeground( mColorLabelDetails ); - } break; } } diff --git a/src/decode/mpt1327/MPT1327ChannelState.java b/src/decode/mpt1327/MPT1327ChannelState.java index 68114d92..43338940 100644 --- a/src/decode/mpt1327/MPT1327ChannelState.java +++ b/src/decode/mpt1327/MPT1327ChannelState.java @@ -23,6 +23,7 @@ import source.config.SourceConfigTuner; import alias.AliasList; import audio.SquelchListener; import audio.SquelchListener.SquelchState; +import controller.activity.CallEvent; import controller.activity.CallEvent.CallEventType; import controller.channel.Channel; import controller.channel.Channel.ChannelType; @@ -43,7 +44,6 @@ public class MPT1327ChannelState extends ChannelState private ChannelMap mChannelMap; private MPT1327ActivitySummary mActivitySummary; - public MPT1327ChannelState( ProcessingChain processingChain, AliasList aliasList, ChannelMap channelMap ) @@ -63,6 +63,12 @@ public class MPT1327ChannelState extends ChannelState mActivitySummary.dispose(); mActivitySummary = null; + mCurrentCallEvent = null; + } + + public void setCurrentCall( MPT1327CallEvent event ) + { + mCurrentCallEvent = event; } @Override @@ -203,8 +209,8 @@ public class MPT1327ChannelState extends ChannelState traffic.addListener( (Listener)this ); CallEventType type = traffic.isProcessing() ? - CallEventType.CALL_START : - CallEventType.CALL_START_NO_TUNER; + CallEventType.CALL : + CallEventType.CALL_NO_TUNER; /* * Set the traffic channel's call event model to @@ -212,15 +218,20 @@ public class MPT1327ChannelState extends ChannelState */ traffic.getProcessingChain().getChannelState() .setCallEventModel( mCallEventModel ); + + MPT1327CallEvent callStartEvent = + new MPT1327CallEvent.Builder( type ) + .aliasList( traffic.getProcessingChain().getAliasList() ) + .channel( channelNumber ) + .frequency( mChannelMap.getFrequency( channelNumber ) ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build(); - mCallEventModel.add( - new MPT1327CallEvent.Builder( type ) - .aliasList( traffic.getProcessingChain().getAliasList() ) - .channel( channelNumber ) - .frequency( mChannelMap.getFrequency( channelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); + traffic.getProcessingChain().getChannelState() + .setCurrentCallEvent( callStartEvent ); + + mCallEventModel.add( callStartEvent ); } } break; @@ -260,14 +271,25 @@ public class MPT1327ChannelState extends ChannelState */ if( getState().canChangeTo( State.FADE ) ) { - mCallEventModel.add( - new MPT1327CallEvent.Builder( type ) - .aliasList( getAliasList() ) - .channel( mChannelNumber ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mFromTalkgroup ) - .to( mToTalkgroup ) - .build() ); + CallEvent current = getCurrentCallEvent(); + + if( current != null ) + { + mCallEventModel.setEnd( current ); + } + else + { + mCallEventModel.add( + new MPT1327CallEvent.Builder( type ) + .aliasList( getAliasList() ) + .channel( mChannelNumber ) + .frequency( mChannelMap.getFrequency( mChannelNumber ) ) + .from( mFromTalkgroup ) + .to( mToTalkgroup ) + .build() ); + } + + setCurrentCall( null ); } super.fade( type ); diff --git a/src/decode/passport/PassportBand.java b/src/decode/passport/PassportBand.java index 8d03d245..f751a4e1 100644 --- a/src/decode/passport/PassportBand.java +++ b/src/decode/passport/PassportBand.java @@ -17,6 +17,8 @@ ******************************************************************************/ package decode.passport; +import log.Log; + public enum PassportBand { BAND_800( "800", 800000000 ), @@ -38,6 +40,8 @@ public enum PassportBand BAND_VHF( "VHF", 0 ), BAND_UNKNOWN( "UNK", 0 ); + public static int CHANNEL_BANDWIDTH = 12500; //Hertz + private String mDescription; private int mBase; @@ -59,6 +63,17 @@ public enum PassportBand } } + public long getFrequency( int channel ) + { + return ( mBase + ( channel * CHANNEL_BANDWIDTH ) ); + } + + public int getChannel( long frequency ) + { + return Math.round( (float)( frequency - mBase ) / + (float)CHANNEL_BANDWIDTH ); + } + public String getDescription() { return mDescription; diff --git a/src/decode/passport/PassportCallEvent.java b/src/decode/passport/PassportCallEvent.java index c81e9803..16a248dc 100644 --- a/src/decode/passport/PassportCallEvent.java +++ b/src/decode/passport/PassportCallEvent.java @@ -26,6 +26,7 @@ public class PassportCallEvent extends CallEvent { private int mChannel; private long mFrequency; + private int mValidCallMessages = 1; public PassportCallEvent( CallEventType callEventType, AliasList aliasList, @@ -42,6 +43,29 @@ public class PassportCallEvent extends CallEvent mFrequency = frequency; } + public boolean addMessage( PassportMessage message ) + { + String tg = message.getToID(); + + if( tg.contentEquals( getToID() ) ) + { + mValidCallMessages++; + return true; + } + + return false; + } + + public boolean isValid() + { + if( getCallEventType() == CallEventType.CALL ) + { + return mValidCallMessages > 1; + } + + return true; + } + private Alias getAlias( String talkgroup ) { if( hasAliasList() ) diff --git a/src/decode/passport/PassportChannelState.java b/src/decode/passport/PassportChannelState.java index c47609d9..1b605ba5 100644 --- a/src/decode/passport/PassportChannelState.java +++ b/src/decode/passport/PassportChannelState.java @@ -17,11 +17,15 @@ ******************************************************************************/ package decode.passport; +import java.util.HashMap; + +import log.Log; import message.Message; import alias.Alias; import alias.AliasList; import audio.AudioType; import audio.SquelchListener; +import controller.activity.CallEvent; import controller.activity.CallEvent.CallEventType; import controller.channel.ProcessingChain; import controller.state.AuxChannelState; @@ -34,9 +38,10 @@ public class PassportChannelState extends ChannelState private String mMobileID; private Alias mMobileIDAlias; private int mChannelNumber; - private long mFrequency; - private PassportCallEvent mCurrentCallEvent; + private int mSiteNumber; + private PassportBand mSiteBand; private PassportActivitySummary mActivitySummary; + private HashMap mActiveCalls = new HashMap(); public PassportChannelState( ProcessingChain channel, AliasList aliasList ) { @@ -44,7 +49,7 @@ public class PassportChannelState extends ChannelState mActivitySummary = new PassportActivitySummary( aliasList ); } - + @Override public String getActivitySummary() { @@ -66,7 +71,7 @@ public class PassportChannelState extends ChannelState public void receive( Message message ) { super.receive( message ); - + if( message instanceof PassportMessage ) { mActivitySummary.receive( message ); @@ -78,44 +83,109 @@ public class PassportChannelState extends ChannelState switch( passport.getMessageType() ) { case CA_STRT: - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - - if( mTalkgroupAlias != null ) + if( mChannelNumber == 0 ) { - try - { - if( mTalkgroupAlias.hasAudioType() ) + mChannelNumber = passport.getLCN(); + } + + if( passport.getLCN() == mChannelNumber ) + { + setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); + setTalkgroupAlias( passport.getTalkgroupIDAlias() ); + + if( mTalkgroupAlias != null ) + { + try { - mAudioTypeListener.setAudioType( - mTalkgroupAlias.getAudioType() ); + if( mTalkgroupAlias.hasAudioType() ) + { + mAudioTypeListener.setAudioType( + mTalkgroupAlias.getAudioType() ); + } } - } - catch( Exception e ) - { - e.printStackTrace(); - } + catch( Exception e ) + { + Log.error( "Error setting audio for " + + "passport call", e ); + } + } + + PassportCallEvent current = + getCurrentPassportCallEvent(); + + /* If we're already in a call event, add the message + * to the current call event ... if false, then we + * have a different call ... cleanup the old one. */ + if( current != null && !current.addMessage( passport ) ) + { + Log.info( "Current call event - testing" ); + + if( current.isValid() ) + { + final PassportCallEvent endEvent = + getCurrentPassportCallEvent(); + + mCallEventModel.setEnd( endEvent ); + } + else + { + mCallEventModel.remove( current ); + } + + current = null; + setCurrentCallEvent( null ); + } + + if( current == null ) + { + mCurrentCallEvent = new PassportCallEvent + .Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( mChannelNumber ) + .frequency( passport.getLCNFrequency() ) + .to( String.valueOf( passport.getTalkgroupID() ) ) + .build(); + + mCallEventModel.add( mCurrentCallEvent ); + } + + State state = getState(); + + if( state == State.IDLE || state == State.CALL ) + { + setState( State.CALL ); + } + else if( state == State.DATA ) + { + setState( State.DATA ); + } } - - mChannelNumber = passport.getLCN(); - mFrequency = passport.getLCNFrequency(); - - if( getState() != State.CALL ) + else { - mCurrentCallEvent = new PassportCallEvent - .Builder( CallEventType.CALL_START ) - .aliasList( mAliasList ) - .channel( mChannelNumber ) - .frequency( mFrequency ) - .to( String.valueOf( passport.getTalkgroupID() ) ) - .build(); + //Call Detection + int lcn = passport.getLCN(); + String tg = String.valueOf( passport.getTalkgroupID() ); - mCallEventModel.add( mCurrentCallEvent ); + if( !mActiveCalls.containsKey( lcn ) || + !mActiveCalls.get( lcn ).contentEquals( tg ) ) + { + mActiveCalls.put( lcn, tg ); + + PassportCallEvent event = new PassportCallEvent + .Builder( CallEventType.CALL_DETECT ) + .aliasList( mAliasList ) + .channel( lcn ) + .details( "Site: " + passport.getSite() ) + .frequency( passport.getLCNFrequency() ) + .to( tg ) + .build(); + + mCallEventModel.add( event ); + } } - - setState( State.CALL ); break; case DA_STRT: +//TODO: add channel number checking and log a call event if on another site setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); setTalkgroupAlias( passport.getTalkgroupIDAlias() ); setState( State.DATA ); @@ -129,12 +199,48 @@ public class PassportChannelState extends ChannelState setMobileID( passport.getMobileID() ); setMobileIDAlias( passport.getMobileIDAlias() ); - if( mCurrentCallEvent != null ) + final String mid = passport.getMobileID(); + + final PassportCallEvent event = + getCurrentPassportCallEvent(); + + if( event != null && mid != null ) { - mCurrentCallEvent.setFromID( mMobileID ); + mCallEventModel.setFromID( event, mid ); } + setState( State.CALL ); break; + case RA_REGI: + mCallEventModel.add( new PassportCallEvent + .Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .channel( passport.getLCN() ) + .frequency( passport.getLCNFrequency() ) + .to( passport.getToID() ) + .build() ); +//TODO: add logic to recognize the register event and suppress the subsequent +//call messages, but capture the group assignments. + setState( State.DATA ); + break; + case SY_IDLE: + PassportBand band = passport.getSiteBand(); + + if( mSiteBand == null || mSiteBand != band ) + { + mSiteBand = band; + + mSiteNumber = passport.getSite(); + + long frequency = mProcessingChain.getChannel() + .getTunerChannel().getFrequency(); + + mChannelNumber = mSiteBand.getChannel( frequency ); + + broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); + } + default: + break; } } } @@ -152,21 +258,33 @@ public class PassportChannelState extends ChannelState */ if( getState().canChangeTo( State.FADE ) ) { - mCallEventModel.add( - new PassportCallEvent.Builder( type ) - .aliasList( getAliasList() ) - .channel( mChannelNumber ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( mTalkgroup ) - .build() ); + final PassportCallEvent current = getCurrentPassportCallEvent(); + + if( current != null ) + { + mCallEventModel.setEnd( current ); + } } + setCurrentCallEvent( null ); + + mActiveCalls.clear(); + super.fade( type ); } + public PassportCallEvent getCurrentPassportCallEvent() + { + CallEvent current = getCurrentCallEvent(); + + if( current != null ) + { + return (PassportCallEvent)current; + } + + return null; + } - /** * Make the ConventionalChannelState always unsquelched */ @@ -190,6 +308,16 @@ public class PassportChannelState extends ChannelState super.reset(); } + public int getChannelNumber() + { + return mChannelNumber; + } + + public int getSiteNumber() + { + return mSiteNumber; + } + public String getTalkgroup() { return mTalkgroup; diff --git a/src/decode/passport/PassportMessage.java b/src/decode/passport/PassportMessage.java index 414964a1..801be7de 100644 --- a/src/decode/passport/PassportMessage.java +++ b/src/decode/passport/PassportMessage.java @@ -207,11 +207,6 @@ public class PassportMessage extends Message return PassportBand.lookup( getInt( sNEIGHBOR_BAND ) ); } - public long getFrequency( int base, int channel ) - { - return ( base + ( channel * 12500 ) ); - } - public int getFree() { return getInt( sFREE ); @@ -233,7 +228,7 @@ public class PassportMessage extends Message { PassportBand band = getNeighborBand(); - return getFrequency( band.getBase(), getFree() ); + return band.getFrequency( getFree() ); } return 0; @@ -377,15 +372,13 @@ public class PassportMessage extends Message @Override public String getToID() { - // TODO Auto-generated method stub - return null; + return String.valueOf( getTalkgroupID() ); } @Override public Alias getToIDAlias() { - // TODO Auto-generated method stub - return null; + return getTalkgroupIDAlias(); } public long getSiteFrequency( int channel ) @@ -396,9 +389,7 @@ public class PassportMessage extends Message if( band != PassportBand.BAND_UNKNOWN ) { - int base = band.getBase(); - - return getFrequency( base, channel ); + return band.getFrequency( channel ); } } @@ -493,7 +484,7 @@ public class PassportMessage extends Message sb.append( getFreeFrequencyFormatted() ); break; case CA_ENDD: - sb.append( "END TG:" ); + sb.append( "END TG:" ); sb.append( format( getTalkgroupID(), 5 ) ); sb.append( "/" ); Alias endAlias = getTalkgroupIDAlias(); diff --git a/src/decode/passport/PassportMessageProcessor.java b/src/decode/passport/PassportMessageProcessor.java index f0a80921..fc7d1454 100644 --- a/src/decode/passport/PassportMessageProcessor.java +++ b/src/decode/passport/PassportMessageProcessor.java @@ -19,6 +19,7 @@ package decode.passport; import java.util.HashMap; +import log.Log; import message.Message; import message.MessageType; import sample.Broadcaster; diff --git a/src/decode/passport/PassportPanel.java b/src/decode/passport/PassportPanel.java index 41e606ad..e99bde91 100644 --- a/src/decode/passport/PassportPanel.java +++ b/src/decode/passport/PassportPanel.java @@ -41,7 +41,7 @@ public class PassportPanel extends ChannelStatePanel private JLabel mToTalkgroup = new JLabel(); private JLabel mToTalkgroupAlias = new JLabel(); - private JLabel mFromTalkgroupType = new JLabel( " " ); + private JLabel mSiteAndChannel = new JLabel( "Site:" ); private JLabel mFromTalkgroup = new JLabel(); private JLabel mFromTalkgroupAlias = new JLabel(); @@ -86,8 +86,8 @@ public class PassportPanel extends ChannelStatePanel mToTalkgroupAlias.setFont( mFontDecoder ); mToTalkgroupAlias.setForeground( mColorLabelDecoder ); - mFromTalkgroupType.setFont( mFontDecoder ); - mFromTalkgroupType.setForeground( mColorLabelDecoder ); + mSiteAndChannel.setFont( mFontDecoder ); + mSiteAndChannel.setForeground( mColorLabelDecoder ); mFromTalkgroup.setFont( mFontDecoder ); mFromTalkgroup.setForeground( mColorLabelDecoder ); @@ -103,7 +103,7 @@ public class PassportPanel extends ChannelStatePanel add( mToTalkgroup ); add( mToTalkgroupAlias, "wrap" ); - add( mFromTalkgroupType ); + add( mSiteAndChannel ); add( mFromTalkgroup ); add( mFromTalkgroupAlias, "wrap" ); } @@ -135,7 +135,16 @@ public class PassportPanel extends ChannelStatePanel mChannelLabel.setText( mChannel.getChannelDisplayName() ); break; case TO_TALKGROUP: - mToTalkgroup.setText( state.getTalkgroup() ); + String tg = state.getTalkgroup(); + + if( tg != null ) + { + mToTalkgroup.setText( "TG:" + tg ); + } + else + { + mToTalkgroup.setText( null ); + } break; case TO_TALKGROUP_ALIAS: Alias toAlias = state.getTalkgroupAlias(); @@ -156,16 +165,14 @@ public class PassportPanel extends ChannelStatePanel case FROM_TALKGROUP: String mid = state.getMobileID(); - mFromTalkgroup.setText( state.getMobileID() ); - - if( mid != null ) - { - mFromTalkgroupType.setText( "Mobile ID" ); - } - else - { - mFromTalkgroupType.setText( " " ); - } + if( mid != null ) + { + mFromTalkgroup.setText( "MIN:" + mid ); + } + else + { + mFromTalkgroup.setText( null ); + } break; case FROM_TALKGROUP_ALIAS: Alias fromAlias = state.getMobileIDAlias(); @@ -183,6 +190,15 @@ public class PassportPanel extends ChannelStatePanel mFromTalkgroupAlias.setIcon( null ); } break; + case CHANNEL_NUMBER: + StringBuilder sb = new StringBuilder(); + sb.append( "Site: " ); + sb.append( state.getSiteNumber() ); + sb.append( "/" ); + sb.append( state.getChannelNumber() ); + + mSiteAndChannel.setText( sb.toString() ); + break; default: break; } @@ -222,9 +238,9 @@ public class PassportPanel extends ChannelStatePanel { mToTalkgroupAlias.setForeground( mColorLabelDecoder ); } - if( mFromTalkgroupType != null ) + if( mSiteAndChannel != null ) { - mFromTalkgroupType.setForeground( mColorLabelDecoder ); + mSiteAndChannel.setForeground( mColorLabelDecoder ); } if( mFromTalkgroup != null ) { diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index f28f3114..2138c768 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -24,7 +24,6 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import log.Log; import sample.Listener; import sample.complex.ComplexSample; import sample.complex.ComplexSampleAssembler; @@ -52,7 +51,6 @@ public class TunerChannelSource extends ComplexSource private TunerChannel mTunerChannel; private Oscillator mSineWaveGenerator; private ComplexFilter[] mDecimationFilters; - private DecimationProcessor mDecimationProcessor = new DecimationProcessor(); private ComplexSampleAssembler mBufferAssembler = new ComplexSampleAssembler( 3000 ); @@ -188,7 +186,7 @@ public class TunerChannelSource extends ComplexSource if( bandwidth != mTunerSampleRate ) { /* Get the original decimation filter output listener */ - Listener listener = null; +// Listener listener = null; /* Get new decimation filters */ mDecimationFilters = FilterFactory @@ -277,9 +275,6 @@ public class TunerChannelSource extends ComplexSource mTunerFrequencyError = error; frequencyChanged( mTunerFrequency, mTunerSampleRate ); } - - Log.info( "TunerChannelSource - mistuned quantity:" + - mTunerFrequencyError + " Hz" ); } @Override diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index c9a68890..9d47d1fa 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -58,7 +58,7 @@ public class DFTProcessor implements Listener, private FFTWidth mNewFFTWidth = FFTWidth.FFT04096; private double[] mWindow; - private WindowType mWindowType = Window.WindowType.BLACKMAN; + private WindowType mWindowType = Window.WindowType.COSINE; private FloatFFT_1D mFFT = new FloatFFT_1D( mFFTWidth.getWidth() );