From 1d77f28958f5327c8ec2cc62689afd6c71fbe16e Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Sun, 21 Dec 2014 14:30:06 +0000 Subject: [PATCH] P25 work in progress - final check-in of channel state and activity summary processing of TSBK and PDU messages and event generation. --- src/controller/ThreadPoolManager.java | 21 +- src/controller/activity/CallEvent.java | 11 +- src/controller/channel/ProcessingChain.java | 4 +- src/decode/fleetsync2/FleetsyncCallEvent.java | 3 +- src/decode/lj1200/LJ1200CallEvent.java | 2 +- src/decode/mdc1200/MDCCallEvent.java | 2 +- src/decode/mpt1327/MPT1327ChannelState.java | 4 +- src/decode/p25/P25ActivitySummary.java | 97 +- src/decode/p25/P25ChannelState.java | 1557 ++++++++++++++--- .../pdu/UnitToUnitChannelGrantExtended.java | 42 +- .../GroupAffiliationResponseExtended.java | 4 +- .../NetworkStatusBroadcastExtended.java | 9 +- .../UnitRegistrationResponseExtended.java | 11 +- .../data/GroupDataChannelGrantExtended.java | 42 +- .../voice/GroupVoiceChannelGrantExplicit.java | 42 +- ...phoneInterconnectChannelGrantExplicit.java | 44 +- .../UnitToUnitAnswerRequestExplicit.java | 4 +- ...ToUnitVoiceChannelGrantUpdateExtended.java | 42 +- src/decode/p25/message/tsbk/ChannelGrant.java | 16 +- .../p25/message/tsbk/GroupChannelGrant.java | 2 +- .../tsbk/GroupChannelGrantExplicit.java | 46 +- .../message/tsbk/GroupMultiChannelGrant.java | 58 +- .../tsbk/osp/control/DenyResponse.java | 27 +- .../osp/control/ExtendedFunctionCommand.java | 39 +- .../osp/control/GroupAffiliationResponse.java | 2 +- .../osp/control/NetworkStatusBroadcast.java | 7 + .../tsbk/osp/control/QueuedResponse.java | 28 +- .../p25/message/tsbk/osp/data/SNDCPData.java | 5 + .../SNDCPDataChannelAnnouncementExplicit.java | 42 +- .../tsbk/osp/data/SNDCPDataChannelGrant.java | 44 +- ...elephoneInterconnectVoiceChannelGrant.java | 9 +- .../voice/UnitToUnitVoiceChannelGrant.java | 2 +- src/decode/p25/reference/Encryption.java | 1 + .../p25/reference/ExtendedFunction.java | 71 + .../p25/reference/P25NetworkCallsign.java | 48 + src/source/tuner/TunerChannelSource.java | 2 +- 36 files changed, 1952 insertions(+), 438 deletions(-) create mode 100644 src/decode/p25/reference/ExtendedFunction.java create mode 100644 src/decode/p25/reference/P25NetworkCallsign.java diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index ba8234bb..5af34435 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -38,7 +38,7 @@ public class ThreadPoolManager { } - public ScheduledFuture schedule( ThreadType type, + public ScheduledFuture scheduleFixedRate( ThreadType type, Runnable command, long period, TimeUnit unit ) @@ -66,6 +66,25 @@ public class ThreadPoolManager } + public void scheduleOnce( Runnable command, long delay, TimeUnit unit ) + throws RejectedExecutionException + { + if( mExecutor == null ) + { + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE ); + } + + if( mTasks.size() <= MAX_TASK_COUNT ) + { + mExecutor.schedule( command, delay, unit ); + } + else + { + throw new RejectedExecutionException( "Cannot schedule task - at " + + "max task count [" + mTasks.size() + "]" ); + } + } + public void cancel( ScheduledFuture task ) { task.cancel( true ); diff --git a/src/controller/activity/CallEvent.java b/src/controller/activity/CallEvent.java index 52a2a568..235e3c5a 100644 --- a/src/controller/activity/CallEvent.java +++ b/src/controller/activity/CallEvent.java @@ -209,24 +209,29 @@ public abstract class CallEvent public enum CallEventType { - ACKNOWLEDGE( "Acknowledge" ), CALL( "Call" ), + CALL_ALERT( "Call Alert" ), CALL_END( "Call End" ), CALL_DETECT( "Call Detect" ), CALL_UNIQUE_ID( "UID Call" ), CALL_NO_TUNER( "Call - No Tuner" ), CALL_TIMEOUT( "Call Timeout" ), + COMMAND( "Command" ), + DATA_CALL( "Data Call" ), DEREGISTER( "Deregister" ), EMERGENCY( "EMERGENCY" ), + FUNCTION( "Function" ), GPS( "GPS" ), ID_ANI( "ANI" ), ID_UNIQUE( "Unique ID" ), PAGE( "Page" ), + QUERY( "Query" ), REGISTER( "Register" ), REGISTER_ESN( "ESN" ), - REQUEST( "Request" ), - SDM( "SDM" ), + RESPONSE( "Response" ), + SDM( "Short Data Message" ), STATUS( "Status" ), + TELEPHONE_CALL( "Telephone Call" ), UNKNOWN( "Unknown" ), INVALID( "Invalid" ); diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java index fc583d90..ba4a2476 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/controller/channel/ProcessingChain.java @@ -283,7 +283,7 @@ public class ProcessingChain implements Listener { mProcessorTask = mResourceManager.getThreadPoolManager() - .schedule( ThreadType.DECODER, + .scheduleFixedRate( ThreadType.DECODER, new ComplexProcessor(), 50, TimeUnit.MILLISECONDS ); @@ -305,7 +305,7 @@ public class ProcessingChain implements Listener { mProcessorTask = mResourceManager.getThreadPoolManager() - .schedule( ThreadType.DECODER, + .scheduleFixedRate( ThreadType.DECODER, new RealProcessor(), 50, TimeUnit.MILLISECONDS ); diff --git a/src/decode/fleetsync2/FleetsyncCallEvent.java b/src/decode/fleetsync2/FleetsyncCallEvent.java index 91b3552c..1c7dd85e 100644 --- a/src/decode/fleetsync2/FleetsyncCallEvent.java +++ b/src/decode/fleetsync2/FleetsyncCallEvent.java @@ -136,7 +136,8 @@ public class FleetsyncCallEvent extends CallEvent switch( message.getMessageType() ) { case ACKNOWLEDGE: - type = CallEventType.ACKNOWLEDGE; + type = CallEventType.RESPONSE; + sbDetails.append( "ACK" ); break; case ANI: type = CallEventType.ID_ANI; diff --git a/src/decode/lj1200/LJ1200CallEvent.java b/src/decode/lj1200/LJ1200CallEvent.java index 670be0b2..a6f52608 100644 --- a/src/decode/lj1200/LJ1200CallEvent.java +++ b/src/decode/lj1200/LJ1200CallEvent.java @@ -128,7 +128,7 @@ public class LJ1200CallEvent extends CallEvent public static LJ1200CallEvent getLJ1200Event( LJ1200Message message ) { - CallEventType type = CallEventType.REQUEST; + CallEventType type = CallEventType.COMMAND; return new LJ1200CallEvent.Builder( type ) .to( message.getAddress() ) diff --git a/src/decode/mdc1200/MDCCallEvent.java b/src/decode/mdc1200/MDCCallEvent.java index 1071aba8..25f8bb8d 100644 --- a/src/decode/mdc1200/MDCCallEvent.java +++ b/src/decode/mdc1200/MDCCallEvent.java @@ -137,7 +137,7 @@ public class MDCCallEvent extends CallEvent switch( message.getMessageType() ) { case ACKNOWLEDGE: - type = CallEventType.ACKNOWLEDGE; + type = CallEventType.RESPONSE; break; case ANI: type = CallEventType.ID_ANI; diff --git a/src/decode/mpt1327/MPT1327ChannelState.java b/src/decode/mpt1327/MPT1327ChannelState.java index 8b39601c..5b3268ee 100644 --- a/src/decode/mpt1327/MPT1327ChannelState.java +++ b/src/decode/mpt1327/MPT1327ChannelState.java @@ -125,7 +125,7 @@ public class MPT1327ChannelState extends ChannelState else { mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.ACKNOWLEDGE ) + new MPT1327CallEvent.Builder( CallEventType.RESPONSE ) .aliasList( getProcessingChain().getAliasList() ) .channel( String.valueOf( mChannelNumber ) ) .details( "ACK " + identType.getLabel() ) @@ -137,7 +137,7 @@ public class MPT1327ChannelState extends ChannelState break; case AHYC: mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.REQUEST ) + new MPT1327CallEvent.Builder( CallEventType.COMMAND ) .aliasList( getProcessingChain().getAliasList() ) .channel( String.valueOf( mChannelNumber ) ) .details( ( (MPT1327Message) message ).getRequestString() ) diff --git a/src/decode/p25/P25ActivitySummary.java b/src/decode/p25/P25ActivitySummary.java index 93f4d470..e5ad2815 100644 --- a/src/decode/p25/P25ActivitySummary.java +++ b/src/decode/p25/P25ActivitySummary.java @@ -18,7 +18,10 @@ package decode.p25; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -33,13 +36,16 @@ import alias.AliasList; import controller.activity.ActivitySummaryProvider; import decode.p25.message.pdu.PDUMessage; import decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; +import decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; import decode.p25.message.tsbk.TSBKMessage; import decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; +import decode.p25.message.tsbk.osp.control.IdentifierUpdate; import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; import decode.p25.message.tsbk.osp.control.SystemService; +import decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; import decode.p25.reference.Service; public class P25ActivitySummary implements ActivitySummaryProvider, @@ -57,14 +63,19 @@ public class P25ActivitySummary implements ActivitySummaryProvider, private NetworkStatusBroadcast mNetworkStatus; private NetworkStatusBroadcastExtended mNetworkStatusExtended; + private ProtectionParameterBroadcast mProtectionParameterBroadcast; private RFSSStatusBroadcast mRFSSStatusMessage; private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; + private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; private HashMap mNeighborMap = new HashMap(); private Set mSecondaryControlChannels = new TreeSet(); + private HashMap mBands = + new HashMap(); + public P25ActivitySummary( AliasList list ) { mAliasList = list; @@ -94,6 +105,23 @@ public class P25ActivitySummary implements ActivitySummaryProvider, mNeighborMap.put( neighborMessage.getUniqueID(), neighborMessage ); } + else if( message instanceof IdentifierUpdate ) + { + IdentifierUpdate iu = (IdentifierUpdate)message; + + if( !mBands.containsKey( iu.getIdentifier() ) ) + { + mBands.put( iu.getIdentifier(), iu ); + } + } + else if( message instanceof NetworkStatusBroadcast ) + { + mNetworkStatus = (NetworkStatusBroadcast)message; + } + else if( message instanceof RFSSStatusBroadcast ) + { + mRFSSStatusMessage = (RFSSStatusBroadcast)message; + } else if( message instanceof SecondaryControlChannelBroadcast ) { SecondaryControlChannelBroadcast sccb = @@ -105,13 +133,9 @@ public class P25ActivitySummary implements ActivitySummaryProvider, (SecondaryControlChannelBroadcast)message ); } } - else if( message instanceof NetworkStatusBroadcast ) + else if( message instanceof SNDCPDataChannelAnnouncementExplicit ) { - mNetworkStatus = (NetworkStatusBroadcast)message; - } - else if( message instanceof RFSSStatusBroadcast ) - { - mRFSSStatusMessage = (RFSSStatusBroadcast)message; + mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)message; } } else if( message instanceof PDUMessage ) @@ -120,6 +144,11 @@ public class P25ActivitySummary implements ActivitySummaryProvider, { mNetworkStatusExtended = (NetworkStatusBroadcastExtended)message; } + else if( message instanceof ProtectionParameterBroadcast ) + { + mProtectionParameterBroadcast = + (ProtectionParameterBroadcast)message; + } else if( message instanceof RFSSStatusBroadcastExtended ) { mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)message; @@ -139,15 +168,21 @@ public class P25ActivitySummary implements ActivitySummaryProvider, if( mNetworkStatus != null ) { sb.append( "\nNAC:\t" + mNetworkStatus.getNAC() ); - sb.append( "\nSYSTEM:\t" + mNetworkStatus.getSystemID() ); - sb.append( "\nWACN:\t" + mNetworkStatus.getWACN() ); + sb.append( "\nWACN-SYS:\t" + mNetworkStatus.getWACN() ); + sb.append( "-" + mNetworkStatus.getSystemID() ); + sb.append( " [" ); + sb.append( mNetworkStatus.getNetworkCallsign() ); + sb.append( "]" ); sb.append( "\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea() ); } else if( mNetworkStatusExtended != null ) { sb.append( "\nNAC:\t" + mNetworkStatusExtended.getNAC() ); - sb.append( "\nSYSTEM:\t" + mNetworkStatusExtended.getSystemID() ); - sb.append( "\nWACN:\t" + mNetworkStatusExtended.getWACN() ); + sb.append( "\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN() ); + sb.append( "-" + mNetworkStatusExtended.getSystemID() ); + sb.append( " [" ); + sb.append( mNetworkStatusExtended.getNetworkCallsign() ); + sb.append( "]" ); sb.append( "\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea() ); } @@ -228,8 +263,48 @@ public class P25ActivitySummary implements ActivitySummaryProvider, } } } - + if( mSNDCPDataChannel != null ) + { + sb.append( "\nSNDCP:" ); + sb.append( "\tDNLINK " + mFrequencyFormatter.format( + (double)mSNDCPDataChannel.getDownlinkFrequency()/1E6D ) + + " [" + mSNDCPDataChannel.getTransmitChannel() + "]" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mSNDCPDataChannel.getUplinkFrequency()/1E6D ) + + " [" + mSNDCPDataChannel.getReceiveChannel() + "]" ); + } + + if( mProtectionParameterBroadcast != null ) + { + sb.append( "\nENCRYPTION:" ); + sb.append( "\nTYPE:\t" + mProtectionParameterBroadcast + .getEncryptionType().name() ); + sb.append( "\nALGORITHM:\t" + mProtectionParameterBroadcast + .getAlgorithmID() ); + sb.append( "\nKEY:\t" + mProtectionParameterBroadcast.getKeyID() ); + sb.append( "\nINBOUND IV:\t" + mProtectionParameterBroadcast + .getInboundInitializationVector() ); + sb.append( "\nOUTBOUND IV:\t" + mProtectionParameterBroadcast + .getOutboundInitializationVector() ); + } + + List identifiers = new ArrayList( mBands.keySet() ); + Collections.sort( identifiers ); + + sb.append( "\nFREQUENCY BANDS:" ); + for( Integer id: identifiers ) + { + IdentifierUpdate band = mBands.get( id ); + + sb.append( "\n\t" + id ); + sb.append( "- BASE: " + mFrequencyFormatter.format( + (double)band.getBaseFrequency() / 1E6d ) ); + sb.append( " CHANNEL SIZE: " + mFrequencyFormatter.format( + (double)band.getChannelSpacing() / 1E6d ) ); + sb.append( " UPLINK OFFSET: " + mFrequencyFormatter.format( + (double)band.getTransmitOffset() / 1E6D ) ); + } sb.append( "\n\n=================== NEIGHBORS ======================" ); diff --git a/src/decode/p25/P25ChannelState.java b/src/decode/p25/P25ChannelState.java index 3883e61b..48e6392e 100644 --- a/src/decode/p25/P25ChannelState.java +++ b/src/decode/p25/P25ChannelState.java @@ -20,37 +20,82 @@ package decode.p25; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.concurrent.TimeUnit; import message.Message; import alias.Alias; import alias.AliasList; import audio.SquelchListener; import audio.SquelchListener.SquelchState; +import controller.ThreadPoolManager; +import controller.activity.CallEvent; import controller.activity.CallEvent.CallEventType; import controller.channel.ProcessingChain; import controller.state.AuxChannelState; import controller.state.ChannelState; import decode.p25.message.P25Message; +import decode.p25.message.pdu.PDUMessage; +import decode.p25.message.pdu.osp.control.CallAlertExtended; +import decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; +import decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; +import decode.p25.message.pdu.osp.control.MessageUpdateExtended; import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; +import decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; +import decode.p25.message.pdu.osp.control.StatusQueryExtended; +import decode.p25.message.pdu.osp.control.StatusUpdateExtended; +import decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; +import decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; +import decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; +import decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; +import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; +import decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; +import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; +import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.message.tsbk.osp.control.AcknowledgeResponse; +import decode.p25.message.tsbk.osp.control.AuthenticationCommand; +import decode.p25.message.tsbk.osp.control.CallAlert; +import decode.p25.message.tsbk.osp.control.DenyResponse; +import decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; +import decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; +import decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; +import decode.p25.message.tsbk.osp.control.MessageUpdate; +import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; +import decode.p25.message.tsbk.osp.control.QueuedResponse; import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; +import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; +import decode.p25.message.tsbk.osp.control.RoamingAddressCommand; +import decode.p25.message.tsbk.osp.control.StatusQuery; +import decode.p25.message.tsbk.osp.control.StatusUpdate; import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; +import decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; +import decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; +import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; +import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; +import decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; +import decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; +import decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; +import decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; +import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; import decode.p25.reference.Opcode; +import decode.p25.reference.Response; import decode.p25.reference.Vendor; public class P25ChannelState extends ChannelState { private HashMap mRegistrations = new HashMap(); - private String mLastDeRegistration; + private String mLastRegistrationEventID; + + private ArrayList mActiveCalls = new ArrayList(); private P25ActivitySummary mActivitySummary; private String mNAC; @@ -85,74 +130,49 @@ public class P25ChannelState extends ChannelState mActivitySummary.receive( (P25Message)message ); } - if( message instanceof RFSSStatusBroadcast ) + if( message instanceof PDUMessage ) { - RFSSStatusBroadcast rfss = (RFSSStatusBroadcast)message; + PDUMessage pdu = (PDUMessage)message; - if( mNAC == null || !mNAC.contentEquals( rfss.getNAC() ) ) + switch( pdu.getOpcode() ) { - mNAC = rfss.getNAC(); - - broadcastChange( ChangedAttribute.NAC ); - } - - if( mSystem == null || !mSystem.contentEquals( rfss.getSystemID() ) ) - { - mSystem = rfss.getSystemID(); - - broadcastChange( ChangedAttribute.SYSTEM ); - } - - String site = rfss.getRFSubsystemID() + "-" + rfss.getSiteID(); - - if( mSite == null || !mSite.contentEquals( site ) ) - { - mSite = site; - - broadcastChange( ChangedAttribute.SITE ); - } - } - else if( message instanceof RFSSStatusBroadcastExtended ) - { - RFSSStatusBroadcastExtended rfss = (RFSSStatusBroadcastExtended)message; - - if( mNAC == null || !mNAC.contentEquals( rfss.getNAC() ) ) - { - mNAC = rfss.getNAC(); - - broadcastChange( ChangedAttribute.NAC ); - } - - if( mSystem == null || !mSystem.contentEquals( rfss.getSystemID() ) ) - { - mSystem = rfss.getSystemID(); - - broadcastChange( ChangedAttribute.SYSTEM ); - } - - String site = rfss.getRFSubsystemID() + "-" + rfss.getSiteID(); - - if( mSite == null || !mSite.contentEquals( site ) ) - { - mSite = site; - - if( mAliasList != null ) - { - Alias alias = mAliasList.getSiteID( mSite ); - - if( alias != null ) - { - mSiteAlias = alias.getName(); - } - else - { - mSiteAlias = null; - } - } - - broadcastChange( ChangedAttribute.SITE ); - - broadcastChange( ChangedAttribute.SITE_ALIAS ); + case CALL_ALERT: + processCall( pdu ); + break; + case GROUP_AFFILIATION_QUERY: + processQuery( pdu ); + break; + case GROUP_AFFILIATION_RESPONSE: + processResponse( pdu ); + break; + case GROUP_DATA_CHANNEL_GRANT: + case GROUP_VOICE_CHANNEL_GRANT: + case INDIVIDUAL_DATA_CHANNEL_GRANT: + processCall( pdu ); + break; + case MESSAGE_UPDATE: + processMessage( pdu ); + break; + case RFSS_STATUS_BROADCAST: + processRFSSStatus( (RFSSStatusBroadcastExtended)message ); + break; + case ROAMING_ADDRESS_UPDATE: + processResponse( pdu ); + break; + case STATUS_QUERY: + processQuery( pdu ); + break; + case STATUS_UPDATE: + processResponse( pdu ); + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + processPage( pdu ); + break; + case UNIT_REGISTRATION_RESPONSE: + processResponse( pdu ); + break; + default: + break; } } else if( message instanceof TSBKMessage ) @@ -165,22 +185,81 @@ public class P25ChannelState extends ChannelState { switch( tsbk.getOpcode() ) { + case ACKNOWLEDGE_RESPONSE: + processResponse( tsbk ); + break; + case AUTHENTICATION_COMMAND: + processCommand( tsbk ); + break; + case CALL_ALERT: + processCall( tsbk ); + break; + case DENY_RESPONSE: + processResponse( tsbk ); + break; + case EXTENDED_FUNCTION_COMMAND: + processCommand( tsbk ); + break; + case GROUP_AFFILIATION_QUERY: + processQuery( tsbk ); + break; + case GROUP_AFFILIATION_RESPONSE: + processResponse( tsbk ); + break; + case GROUP_DATA_CHANNEL_ANNOUNCEMENT: + case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + case GROUP_DATA_CHANNEL_GRANT: case GROUP_VOICE_CHANNEL_GRANT: case GROUP_VOICE_CHANNEL_GRANT_UPDATE: case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + case INDIVIDUAL_DATA_CHANNEL_GRANT: case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: processCall( tsbk ); break; + case LOCATION_REGISTRATION_RESPONSE: + case UNIT_DEREGISTRATION_ACKNOWLEDGE: + processResponse( tsbk ); + break; + case MESSAGE_UPDATE: + processMessage( tsbk ); + break; + case PROTECTION_PARAMETER_UPDATE: + processResponse( tsbk ); + break; + case QUEUED_RESPONSE: + processResponse( tsbk ); + break; + case RADIO_UNIT_MONITOR_COMMAND: + processCommand( tsbk ); + break; + case RFSS_STATUS_BROADCAST: + processRFSSStatus( (RFSSStatusBroadcast)tsbk ); + break; + case ROAMING_ADDRESS_COMMAND: + processCommand( tsbk ); + break; + case SNDCP_DATA_CHANNEL_GRANT: + processCall( tsbk ); + break; + case STATUS_QUERY: + processQuery( tsbk ); + break; + case STATUS_UPDATE: + processResponse( tsbk ); + break; case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: case UNIT_TO_UNIT_ANSWER_REQUEST: processPage( tsbk ); break; - case LOCATION_REGISTRATION_RESPONSE: - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - processRegistration( tsbk ); + case UNIT_REGISTRATION_COMMAND: + processCommand( tsbk ); + break; + case UNIT_REGISTRATION_RESPONSE: + processResponse( tsbk ); + break; default: break; } @@ -188,6 +267,720 @@ public class P25ChannelState extends ChannelState } } + private void processCommand( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case AUTHENTICATION_COMMAND: + AuthenticationCommand ac = (AuthenticationCommand)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( mAliasList ) + .details( "AUTHENTICATE" ) + .to( ac.getWACN() + "-" + ac.getSystemID() + "-" + + ac.getTargetID() ) + .build() ); + break; + case EXTENDED_FUNCTION_COMMAND: + ExtendedFunctionCommand efc = (ExtendedFunctionCommand)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.FUNCTION ) + .aliasList( mAliasList ) + .details( "EXTENDED FUNCTION: " + + efc.getExtendedFunction().getLabel() ) + .from( efc.getSourceAddress() ) + .to( efc.getTargetAddress() ) + .build() ); + break; + case RADIO_UNIT_MONITOR_COMMAND: + RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( mAliasList ) + .details( "RADIO UNIT MONITOR" ) + .from( rumc.getSourceAddress() ) + .to( rumc.getTargetAddress() ) + .build() ); + break; + case ROAMING_ADDRESS_COMMAND: + RoamingAddressCommand rac = (RoamingAddressCommand)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( mAliasList ) + .details( rac.getStackOperation().name() + + " ROAMING ADDRESS " + rac.getWACN() + "-" + + rac.getSystemID() ) + .to( rac.getTargetID() ) + .build() ); + break; + case UNIT_REGISTRATION_COMMAND: + UnitRegistrationCommand urc = (UnitRegistrationCommand)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( mAliasList ) + .details( "REGISTER" ) + .from( urc.getSourceAddress() ) + .to( urc.getTargetAddress() ) + .build() ); + break; + default: + break; + } + } + + private void processMessage( TSBKMessage message ) + { + MessageUpdate mu = (MessageUpdate)message; + + CallEvent event = + new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( mAliasList ) + .details( "MESSAGE: " + mu.getMessage() ) + .from( mu.getSourceAddress() ) + .to( mu.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + } + + private void processMessage( PDUMessage message ) + { + MessageUpdateExtended mu = (MessageUpdateExtended)message; + + CallEvent event = + new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( mAliasList ) + .details( "MESSAGE: " + mu.getMessage() ) + .from( mu.getSourceWACN() + "-" + mu.getSourceSystemID() + + "-" + mu.getSourceID() ) + .to( mu.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + } + + private void processQuery( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case GROUP_AFFILIATION_QUERY: + GroupAffiliationQuery gaq = (GroupAffiliationQuery)message; + + CallEvent event = + new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( mAliasList ) + .details( "GROUP AFFILIATION" ) + .from( gaq.getSourceAddress() ) + .to( gaq.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + break; + case STATUS_QUERY: + StatusQuery sq = (StatusQuery)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( mAliasList ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + break; + default: + break; + } + } + + private void processQuery( PDUMessage message ) + { + switch( message.getOpcode() ) + { + case GROUP_AFFILIATION_QUERY: + GroupAffiliationQueryExtended gaqe = + (GroupAffiliationQueryExtended)message; + + CallEvent event = + new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( mAliasList ) + .details( "GROUP AFFILIATION" ) + .from( gaqe.getWACN() + "-" + gaqe.getSystemID() + + "-" + gaqe.getSourceID() ) + .to( gaqe.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + break; + case STATUS_QUERY: + StatusQueryExtended sq = (StatusQueryExtended)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( mAliasList ) + .details( "STATUS QUERY" ) + .from( sq.getSourceWACN() + "-" + + sq.getSourceSystemID() + "-" + + sq.getSourceID() ) + .to( sq.getTargetAddress() ) + .build() ); + break; + default: + break; + } + } + + private void processResponse( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case ACKNOWLEDGE_RESPONSE: + AcknowledgeResponse ar = (AcknowledgeResponse)message; + + CallEvent event = + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "ACKNOWLEDGE" ) + .to( ar.getTargetAddress() ) + .build(); + + if( ar.hasAdditionalInformation() ) + { + if( ar.hasExtendedAddress() ) + { + event.setToID( ar.getWACN() + "-" + ar.getSystemID() + "-" + + ar.getTargetAddress() ); + } + else + { + event.setFromID( ar.getFromID() ); + } + } + + mCallEventModel.add( event ); + break; + case DENY_RESPONSE: + DenyResponse dr = (DenyResponse)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "DENY REASON: " + dr.getReason().name() + + " REQUESTED: " + dr.getServiceType().name() ) + .from( dr.getSourceAddress() ) + .to( dr.getTargetAddress() ) + .build() ); + break; + case GROUP_AFFILIATION_RESPONSE: + GroupAffiliationResponse gar = (GroupAffiliationResponse)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "AFFILIATION:" + gar.getResponse().name() + + " FOR " + gar.getAffiliationScope() + + " GROUP:" + gar.getGroupAddress() + + " GROUP ANNOUNCEMENT:" + + gar.getAnnouncementGroupAddress() ) + .to( gar.getTargetAddress() ) + .build() ); + + break; + case LOCATION_REGISTRATION_RESPONSE: + LocationRegistrationResponse lrr = + (LocationRegistrationResponse)message; + + if( lrr.getResponse() == Response.ACCEPT ) + { + mRegistrations.put( lrr.getTargetAddress(), + System.currentTimeMillis() ); + } + + if( mLastRegistrationEventID != null && + !mLastRegistrationEventID.contentEquals( lrr.getTargetAddress() ) ) + { + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .details( "REGISTRATION:" + lrr.getResponse().name() + + " SITE: " + lrr.getRFSSID() + "-" + + lrr.getSiteID() ) + .from( lrr.getTargetAddress() ) + .to( lrr.getGroupAddress() ) + .build() ); + } + + mLastRegistrationEventID = lrr.getTargetAddress(); + break; + case PROTECTION_PARAMETER_UPDATE: + ProtectionParameterUpdate ppu = (ProtectionParameterUpdate)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "USE ENCRYPTION ALGORITHM:" + + ppu.getAlgorithm().name() + " KEY:" + + ppu.getKeyID() ) + .to( ppu.getTargetAddress() ) + .build() ); + break; + case QUEUED_RESPONSE: + QueuedResponse qr = (QueuedResponse)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "QUEUED REASON: " + qr.getReason().name() + + " REQUESTED: " + qr.getServiceType().name() ) + .from( qr.getSourceAddress() ) + .to( qr.getTargetAddress() ) + .build() ); + break; + case STATUS_UPDATE: + StatusUpdate su = (StatusUpdate)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "STATUS USER: " + su.getUserStatus() + + " UNIT: " + su.getUnitStatus() ) + .from( su.getSourceAddress() ) + .to( su.getTargetAddress() ) + .build() ); + break; + case UNIT_REGISTRATION_RESPONSE: + UnitRegistrationResponse urr = (UnitRegistrationResponse)message; + + if( urr.getResponse() == Response.ACCEPT ) + { + mRegistrations.put( urr.getSourceAddress(), + System.currentTimeMillis() ); + } + + if( mLastRegistrationEventID != null && + !mLastRegistrationEventID.contentEquals( urr.getSourceAddress() ) ) + { + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .details( "REGISTRATION:" + urr.getResponse().name() + + " SYSTEM: " + urr.getSystemID() + "-" + + " SOURCE ID: " + urr.getSourceID() ) + .from( urr.getSourceAddress() ) + .build() ); + } + + mLastRegistrationEventID = urr.getSourceAddress(); + break; + case UNIT_DEREGISTRATION_ACKNOWLEDGE: + UnitDeregistrationAcknowledge udr = + (UnitDeregistrationAcknowledge)message; + + if( mLastRegistrationEventID != null && + !mLastRegistrationEventID.contentEquals( udr.getSourceID() ) ) + { + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.DEREGISTER ) + .aliasList( mAliasList ) + .from( udr.getSourceID() ) + .build() ); + + List keysToRemove = new ArrayList(); + + /* Remove this radio from the registrations set */ + for( String key: mRegistrations.keySet() ) + { + if( key.startsWith( udr.getSourceID() ) ) + { + keysToRemove.add( key ); + } + } + + for( String key: keysToRemove ) + { + mRegistrations.remove( key ); + } + } + + mLastRegistrationEventID = udr.getSourceID(); + break; + default: + break; + } + } + + private void processResponse( PDUMessage message ) + { + switch( message.getOpcode() ) + { + case GROUP_AFFILIATION_RESPONSE: + GroupAffiliationResponseExtended gar = + (GroupAffiliationResponseExtended)message; + + CallEvent event = + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "AFFILIATION:" + gar.getResponse().name() + + " FOR GROUP:" + gar.getGroupWACN() + "-" + + gar.getGroupSystemID() + "-" + + gar.getGroupID() + " GROUP ANNOUNCEMENT:" + + gar.getAnnouncementGroupID() ) + .from( gar.getSourceWACN() + "-" + + gar.getSourceSystemID() + "-" + + gar.getSourceID() ) + .to( gar.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + break; + case ROAMING_ADDRESS_UPDATE: + RoamingAddressUpdateExtended raue = + (RoamingAddressUpdateExtended)message; + + StringBuilder sb = new StringBuilder(); + sb.append( "ROAMING ADDRESS STACK A:" ); + sb.append( raue.getWACNA() + "-" + raue.getSystemIDA() ); + + if( raue.isFormat2() ) + { + sb.append( " B:" ); + sb.append( raue.getWACNB() + "-" + raue.getSystemIDB() ); + sb.append( " C:" ); + sb.append( raue.getWACNC() + "-" + raue.getSystemIDC() ); + sb.append( " D:" ); + sb.append( raue.getWACND() + "-" + raue.getSystemIDD() ); + } + + if( raue.isFormat3() ) + { + sb.append( " E:" ); + sb.append( raue.getWACNE() + "-" + raue.getSystemIDE() ); + sb.append( " F:" ); + sb.append( raue.getWACNF() + "-" + raue.getSystemIDF() ); + sb.append( " G:" ); + sb.append( raue.getWACNG() + "-" + raue.getSystemIDG() ); + } + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( sb.toString() ) + .from( raue.getSourceID() ) + .to( raue.getTargetAddress() ) + .build() ); + break; + case STATUS_UPDATE: + StatusUpdateExtended su = (StatusUpdateExtended)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( mAliasList ) + .details( "STATUS USER: " + su.getUserStatus() + + " UNIT: " + su.getUnitStatus() ) + .from( su.getSourceWACN() + "-" + + su.getSourceSystemID() + "-" + + su.getSourceID() ) + .to( su.getTargetAddress() ) + .build() ); + break; + case UNIT_REGISTRATION_RESPONSE: + UnitRegistrationResponseExtended urr = + (UnitRegistrationResponseExtended)message; + + if( urr.getResponse() == Response.ACCEPT ) + { + mRegistrations.put( urr.getAssignedSourceAddress(), + System.currentTimeMillis() ); + } + + if( mLastRegistrationEventID != null && + !mLastRegistrationEventID.contentEquals( urr.getAssignedSourceAddress() ) ) + { + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( mAliasList ) + .details( "REGISTRATION:" + urr.getResponse().name() + + " FOR EXTERNAL SYSTEM ADDRESS: " + + urr.getWACN() + "-" + + urr.getSystemID() + "-" + + urr.getSourceAddress() + + " SOURCE ID: " + urr.getSourceID() ) + .from( urr.getAssignedSourceAddress() ) + .build() ); + } + + mLastRegistrationEventID = urr.getAssignedSourceAddress(); + break; + default: + break; + } + } + + private void processRFSSStatus( RFSSStatusBroadcast message ) + { + if( mNAC == null || !mNAC.contentEquals( message.getNAC() ) ) + { + mNAC = message.getNAC(); + + broadcastChange( ChangedAttribute.NAC ); + } + + if( mSystem == null || !mSystem.contentEquals( message.getSystemID() ) ) + { + mSystem = message.getSystemID(); + + broadcastChange( ChangedAttribute.SYSTEM ); + } + + String site = message.getRFSubsystemID() + "-" + message.getSiteID(); + + if( mSite == null || !mSite.contentEquals( site ) ) + { + mSite = site; + + broadcastChange( ChangedAttribute.SITE ); + } + } + + private void processRFSSStatus( RFSSStatusBroadcastExtended message ) + { + if( mNAC == null || !mNAC.contentEquals( message.getNAC() ) ) + { + mNAC = message.getNAC(); + + broadcastChange( ChangedAttribute.NAC ); + } + + if( mSystem == null || !mSystem.contentEquals( message.getSystemID() ) ) + { + mSystem = message.getSystemID(); + + broadcastChange( ChangedAttribute.SYSTEM ); + } + + String site = message.getRFSubsystemID() + "-" + message.getSiteID(); + + if( mSite == null || !mSite.contentEquals( site ) ) + { + mSite = site; + + if( mAliasList != null ) + { + Alias alias = mAliasList.getSiteID( mSite ); + + if( alias != null ) + { + mSiteAlias = alias.getName(); + } + else + { + mSiteAlias = null; + } + } + + broadcastChange( ChangedAttribute.SITE ); + + broadcastChange( ChangedAttribute.SITE_ALIAS ); + } + } + + private void processCall( PDUMessage message ) + { + switch( message.getOpcode() ) + { + case CALL_ALERT: + CallAlertExtended ca = (CallAlertExtended)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL_ALERT ) + .aliasList( mAliasList ) + .from( ca.getWACN() + "-" + ca.getSystemID() + "-" + + ca.getSourceID() ) + .to( ca.getTargetAddress() ) + .build() ); + break; + case GROUP_DATA_CHANNEL_GRANT: + GroupDataChannelGrantExtended gdcge = + (GroupDataChannelGrantExtended)message; + + if( isActiveCall( gdcge.getTransmitChannel(), gdcge.getGroupAddress() ) ) + { + updateCall( gdcge.getTransmitChannel(), gdcge.getGroupAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( gdcge.getTransmitChannel() ) + .details( "GROUP DATA CHANNEL GRANT EXTENDED" ) +// .details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gdcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcge.getDownlinkFrequency() ) + .from( gdcge.getSourceAddress() ) + .to( gdcge.getGroupAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case GROUP_VOICE_CHANNEL_GRANT: + GroupVoiceChannelGrantExplicit gvcge = + (GroupVoiceChannelGrantExplicit)message; + + if( isActiveCall( gvcge.getTransmitChannel(), gvcge.getGroupAddress() ) ) + { + updateCall( gvcge.getTransmitChannel(), gvcge.getGroupAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcge.getTransmitChannel() ) + .details( "GROUP_VOICE_CHANNEL_GRANT" ) +// .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcge.getDownlinkFrequency() ) + .from( gvcge.getSourceAddress() ) + .to( gvcge.getGroupAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case INDIVIDUAL_DATA_CHANNEL_GRANT: + IndividualDataChannelGrantExtended idcge = + (IndividualDataChannelGrantExtended)message; + + if( isActiveCall( idcge.getTransmitChannel(), idcge.getTargetAddress() ) ) + { + updateCall( idcge.getTransmitChannel(), idcge.getTargetAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( idcge.getTransmitChannel() ) + .details( "INDIV DATA CHANNEL GRANT EXTENDED" ) +// .details( ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( idcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( idcge.getDownlinkFrequency() ) + .from( idcge.getSourceWACN() + "-" + + idcge.getSourceSystemID() + "-" + + idcge.getSourceAddress() ) + .to( idcge.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + TelephoneInterconnectChannelGrantExplicit ticge = + (TelephoneInterconnectChannelGrantExplicit)message; + + if( isActiveCall( ticge.getTransmitChannel(), ticge.getAddress() )) + { + updateCall( ticge.getTransmitChannel(), ticge.getAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( mAliasList ) + .channel( ticge.getTransmitChannel() ) + .details( ( ticge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( ticge.isEmergency() ? " EMERGENCY" : "") + + " CALL TIMER:" + ticge.getCallTimer() ) + .frequency( ticge.getDownlinkFrequency() ) + .from( ticge.getAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + UnitToUnitVoiceChannelGrantExtended uuvcge = + (UnitToUnitVoiceChannelGrantExtended)message; + + if( isActiveCall( uuvcge.getTransmitChannel(), uuvcge.getTargetAddress() ) ) + { + updateCall( uuvcge.getTransmitChannel(), uuvcge.getTargetAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( uuvcge.getTransmitChannel() ) + .details( "UNIT_TO_UNIT_VOICE_CHANNEL_GRANT" ) +// .details( ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( uuvcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcge.getDownlinkFrequency() ) + .from( uuvcge.getSourceWACN() + "-" + + uuvcge.getSourceSystemID() + "-" + + uuvcge.getSourceID() ) + .to( uuvcge.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = + (UnitToUnitVoiceChannelGrantUpdateExtended)message; + + if( isActiveCall( uuvcgue.getTransmitChannel(), + uuvcgue.getTargetAddress() ) ) + { + updateCall( uuvcgue.getTransmitChannel(), + uuvcgue.getTargetAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( uuvcgue.getTransmitChannel() ) + .details( "UNIT_TO_UNIT_VOICE_CHANNEL_GRANT" ) + // .details( ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) + + // ( uuvcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgue.getDownlinkFrequency() ) + .from( uuvcgue.getSourceWACN() + "-" + + uuvcgue.getSourceSystemID() + "-" + + uuvcgue.getSourceID() ) + .to( uuvcgue.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + + break; + + default: + break; + } + + } + /** * Process a call event message */ @@ -195,43 +988,193 @@ public class P25ChannelState extends ChannelState { switch( message.getOpcode() ) { + case CALL_ALERT: + CallAlert ca = (CallAlert)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.CALL_ALERT ) + .aliasList( mAliasList ) + .from( ca.getSourceID() ) + .to( ca.getTargetAddress() ) + .build() ); + break; + case GROUP_DATA_CHANNEL_ANNOUNCEMENT: + GroupDataChannelAnnouncement gdca = (GroupDataChannelAnnouncement)message; + + if( isActiveCall( gdca.getChannel1(), gdca.getGroupAddress1() ) ) + { + updateCall( gdca.getChannel1(), gdca.getGroupAddress1() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( gdca.getChannel1() ) + .details( "GROUP DATA CHANNEL ANNOUNCEMENT" ) +// .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gdca.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdca.getDownlinkFrequency1() ) + .to( gdca.getGroupAddress1() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + + if( gdca.hasChannelNumber2() ) + { + if( isActiveCall( gdca.getChannel2(), gdca.getGroupAddress2() ) ) + { + updateCall( gdca.getChannel2(), gdca.getGroupAddress2() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( gdca.getChannel2() ) + .details( "GROUP DATA CHANNEL ANNOUNCEMENT" ) +// .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gdca.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdca.getDownlinkFrequency2() ) + .to( gdca.getGroupAddress2() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + } + break; + case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + GroupDataChannelAnnouncementExplicit gdcae = + (GroupDataChannelAnnouncementExplicit)message; + + if( isActiveCall( gdcae.getTransmitChannel(), gdcae.getGroupAddress() ) ) + { + updateCall( gdcae.getTransmitChannel(), gdcae.getGroupAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( gdcae.getTransmitChannel() ) + .details( "GROUP DATA CHANNEL ANNOUNCEMENT EXPLICIT" ) +// .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gdca.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcae.getDownlinkFrequency() ) + .to( gdcae.getGroupAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case GROUP_DATA_CHANNEL_GRANT: + GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; + + if( isActiveCall( gdcg.getChannel(), gdcg.getGroupAddress() ) ) + { + updateCall( gdcg.getChannel(), gdcg.getGroupAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( gdcg.getChannel() ) + .details( "GROUP DATA CHANNEL GRANT" ) +// .details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gdcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcg.getDownlinkFrequency() ) + .from( gdcg.getSourceAddress() ) + .to( gdcg.getGroupAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; case GROUP_VOICE_CHANNEL_GRANT: GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcg.getChannelIdentifier() + "-" + gvcg.getChannel() ) - .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcg.getDownlinkFrequency() ) - .from( gvcg.getSourceAddress() ) - .to( gvcg.getGroupAddress() ) - .build() ); + if( isActiveCall( gvcg.getChannel(), gvcg.getGroupAddress() ) ) + { + updateCall( gvcg.getChannel(), gvcg.getGroupAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcg.getChannel() ) + .details( "GROUP_VOICE_CHANNEL_GRANT" ) +// .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcg.getDownlinkFrequency() ) + .from( gvcg.getSourceAddress() ) + .to( gvcg.getGroupAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } break; case GROUP_VOICE_CHANNEL_GRANT_UPDATE: GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)message; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannelID1() + "-" + gvcgu.getChannelNumber1() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency1() ) - .to( gvcgu.getGroupAddress1() ) - .build() ); - if( gvcgu.hasChannelNumber2() ) + + if( isActiveCall( gvcgu.getChannel1(), gvcgu.getGroupAddress1() ) ) { - mCallEventModel.add( + updateCall( gvcgu.getChannel1(), gvcgu.getGroupAddress1() ); + } + else + { + CallEvent event = new P25CallEvent.Builder( CallEventType.CALL ) .aliasList( mAliasList ) - .channel( gvcgu.getChannelID2() + "-" + gvcgu.getChannelNumber2() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency2() ) - .to( gvcgu.getGroupAddress2() ) - .build() ); + .channel( gvcgu.getChannel1() ) + .details( "1-GROUP_VOICE_CHANNEL_GRANT_UPDATE" ) +// .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency1() ) + .to( gvcgu.getGroupAddress1() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + + if( gvcgu.hasChannelNumber2() ) + { + if( isActiveCall( gvcgu.getChannel2(), gvcgu.getGroupAddress2() ) ) + { + updateCall( gvcgu.getChannel2(), gvcgu.getGroupAddress2() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( gvcgu.getChannel2() ) + .details( "2-GROUP_VOICE_CHANNEL_GRANT_UPDATE" ) +// .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency2() ) + .to( gvcgu.getGroupAddress2() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } } break; case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: @@ -241,187 +1184,267 @@ public class P25ChannelState extends ChannelState mCallEventModel.add( new P25CallEvent.Builder( CallEventType.CALL ) .aliasList( mAliasList ) - .channel( gvcgue.getTransmitChannelID() + "-" + + .channel( gvcgue.getTransmitChannelIdentifier() + "-" + gvcgue.getTransmitChannelNumber() ) - .details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) + .details( "GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT") +// .details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) .frequency( gvcgue.getDownlinkFrequency() ) .to( gvcgue.getGroupAddress() ) .build() ); break; + case INDIVIDUAL_DATA_CHANNEL_GRANT: + IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; + + if( isActiveCall( idcg.getChannel(), idcg.getTargetAddress() ) ) + { + updateCall( idcg.getChannel(), idcg.getTargetAddress() ); + } + else + { + CallEvent event = + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( idcg.getChannel() ) + .details( "INDIV DATA CHANNEL GRANT" ) +// .details( ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( idcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( idcg.getDownlinkFrequency() ) + .from( idcg.getSourceAddress() ) + .to( idcg.getTargetAddress() ) + .build(); + + mCallEventModel.add( event ); + + addCall( event ); + } + break; + case SNDCP_DATA_CHANNEL_GRANT: + SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( mAliasList ) + .channel( sdcg.getTransmitChannel() ) + .details( "SNDCP DATA NSAPI:" + sdcg.getNSAPI() ) + .frequency( sdcg.getDownlinkFrequency() ) + .to( sdcg.getTargetAddress() ) + .build() ); + break; case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: TelephoneInterconnectVoiceChannelGrant tivcg = (TelephoneInterconnectVoiceChannelGrant)message; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( tivcg.getChannelIdentifier() + "-" + - tivcg.getChannel() ) - .details( ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( tivcg.getDownlinkFrequency() ) - .from( tivcg.getSourceAddress() ) - .to( tivcg.getTargetAddress() ) - .build() ); + if( isActiveCall( tivcg.getChannel(), tivcg.getAddress() )) + { + updateCall( tivcg.getChannel(), tivcg.getAddress() ); + } + else + { + CallEvent tivcgEvent = + new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( mAliasList ) + .channel( tivcg.getChannel() ) + .details( ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcg.isEmergency() ? " EMERGENCY" : "") + + " CALL TIMER:" + tivcg.getCallTimer() ) + .frequency( tivcg.getDownlinkFrequency() ) + .from( tivcg.getAddress() ) + .build(); + + mCallEventModel.add( tivcgEvent ); + + addCall( tivcgEvent ); + } break; case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = (TelephoneInterconnectVoiceChannelGrantUpdate)message; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( tivcgu.getChannelIdentifier() + "-" + - tivcgu.getChannel() ) - .details( ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( tivcgu.getDownlinkFrequency() ) - .from( tivcgu.getSourceAddress() ) - .to( tivcgu.getTargetAddress() ) - .build() ); + + if( isActiveCall( tivcgu.getChannel(), tivcgu.getAddress() ) ) + { + updateCall( tivcgu.getChannel(), tivcgu.getAddress() ); + } + else + { + CallEvent tivcguEvent = + new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( mAliasList ) + .channel( tivcgu.getChannelIdentifier() + "-" + + tivcgu.getChannelNumber() ) + .details( "TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE" ) +// .details( ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( tivcgu.getDownlinkFrequency() ) + .from( tivcgu.getAddress() ) + .build(); + + mCallEventModel.add( tivcguEvent ); + + addCall( tivcguEvent ); + } break; case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: UnitToUnitVoiceChannelGrant uuvcg = (UnitToUnitVoiceChannelGrant)message; + + if( isActiveCall( uuvcg.getChannel(), uuvcg.getTargetAddress() ) ) + { + updateCall( uuvcg.getChannel(), uuvcg.getTargetAddress() ); + } + else + { + CallEvent uuvcgEvent = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( uuvcg.getChannelIdentifier() + "-" + + uuvcg.getChannelNumber() ) + .details( "UNIT_TO_UNIT_VOICE_CHANNEL_GRANT" ) +// .details( ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcg.getDownlinkFrequency() ) + .from( uuvcg.getSourceAddress() ) + .to( uuvcg.getTargetAddress() ) + .build(); + + mCallEventModel.add( uuvcgEvent ); + + addCall( uuvcgEvent ); + } - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcg.getChannelIdentifier() + "-" + - uuvcg.getChannel() ) - .details( ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcg.getDownlinkFrequency() ) - .from( uuvcg.getSourceAddress() ) - .to( uuvcg.getTargetAddress() ) - .build() ); break; case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: UnitToUnitVoiceChannelGrantUpdate uuvcgu = (UnitToUnitVoiceChannelGrantUpdate)message; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcgu.getChannelIdentifier() + "-" + - uuvcgu.getChannel() ) - .details( ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcgu.getDownlinkFrequency() ) - .from( uuvcgu.getSourceAddress() ) - .to( uuvcgu.getTargetAddress() ) - .build() ); + if( isActiveCall( uuvcgu.getChannel(), uuvcgu.getTargetAddress() ) ) + { + updateCall( uuvcgu.getChannel(), uuvcgu.getTargetAddress() ); + } + else + { + CallEvent uuvcguEvent = + new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( mAliasList ) + .channel( uuvcgu.getChannelIdentifier() + "-" + + uuvcgu.getChannelNumber() ) + .details( "UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE" ) +// .details( ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + +// ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgu.getDownlinkFrequency() ) + .from( uuvcgu.getSourceAddress() ) + .to( uuvcgu.getTargetAddress() ) + .build(); + + mCallEventModel.add( uuvcguEvent ); + + addCall( uuvcguEvent ); + } + break; default: break; } } + + private boolean isActiveCall( String channel, String id ) + { + for( ActiveCall call: mActiveCalls ) + { + if( call.matches( channel, id ) ) + { + return true; + } + } + + return false; + } + + private void addCall( CallEvent event ) + { + mActiveCalls.add( new ActiveCall( getProcessingChain() + .getResourceManager().getThreadPoolManager(), event ) ); + } + + private void updateCall( String channel, String id ) + { + for( ActiveCall call: mActiveCalls ) + { + if( call.matches( channel, id ) ) + { + call.update( System.currentTimeMillis() ); + + return; + } + } + } /** * Process a unit paging event message */ private void processPage( TSBKMessage message ) - { - if( message.getOpcode() == Opcode.UNIT_TO_UNIT_ANSWER_REQUEST ) - { - UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) ) - .from( utuar.getSourceAddress() ) - .to( utuar.getTargetAddress() ) - .build() ); - } - } - - /** - * Process a unit registration or deregistration message - */ - private void processRegistration( TSBKMessage message ) { switch( message.getOpcode() ) { - case LOCATION_REGISTRATION_RESPONSE: - LocationRegistrationResponse lrr = - (LocationRegistrationResponse)message; - - String addressPlusGroup = lrr.getTargetAddress() + - lrr.getGroupAddress(); - - boolean updateEvent = false; - - if( mRegistrations.containsKey( addressPlusGroup ) ) - { - long timestamp = mRegistrations.get( addressPlusGroup ); - - updateEvent = ( System.currentTimeMillis() - - timestamp ) > 300000; //5 minutes - } - else - { - updateEvent = true; - } - - mRegistrations.put( addressPlusGroup, System.currentTimeMillis() ); - - if( updateEvent ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "RESPONSE:" + lrr.getResponse().name() + - " SITE: " + lrr.getRFSSID() + "-" + - lrr.getSiteID() ) - .from( lrr.getTargetAddress() ) - .to( lrr.getGroupAddress() ) - .build() ); - } - - /* Reset the deregistration variable */ - mLastDeRegistration = null; - break; - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - UnitDeregistrationAcknowledge udr = - (UnitDeregistrationAcknowledge)message; - - if( mLastDeRegistration != null && - mLastDeRegistration.contentEquals( udr.getSourceID() ) ) - { - break; //Suppress duplicate event - } - - mLastDeRegistration = udr.getSourceID(); + case UNIT_TO_UNIT_ANSWER_REQUEST: + UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.DEREGISTER ) + new P25CallEvent.Builder( CallEventType.PAGE ) .aliasList( mAliasList ) - .from( udr.getSourceID() ) + .details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) ) + .from( utuar.getSourceAddress() ) + .to( utuar.getTargetAddress() ) .build() ); - - List keysToRemove = new ArrayList(); - - /* Remove this radio from the registrations set */ - for( String key: mRegistrations.keySet() ) - { - if( key.startsWith( udr.getSourceID() ) ) - { - keysToRemove.add( key ); - } - } - - for( String key: keysToRemove ) - { - mRegistrations.remove( key ); - } + break; + case SNDCP_DATA_PAGE_REQUEST: + SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)message; + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( mAliasList ) + .details( "SNDCP DATA DAC: " + + sdpr.getDataAccessControl() + + " NSAPI:" + sdpr.getNSAPI() ) + .to( sdpr.getTargetAddress() ) + .build() ); + break; + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + TelephoneInterconnectAnswerRequest tiar = + (TelephoneInterconnectAnswerRequest)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( mAliasList ) + .details( ( "TELEPHONE INTERCONNECT" ) ) + .from( tiar.getTelephoneNumber() ) + .to( tiar.getTargetAddress() ) + .build() ); break; default: break; } } + private void processPage( PDUMessage message ) + { + if( message.getOpcode() == Opcode.UNIT_TO_UNIT_ANSWER_REQUEST ) + { + UnitToUnitAnswerRequestExplicit utuare = + (UnitToUnitAnswerRequestExplicit)message; + + mCallEventModel.add( + new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( mAliasList ) + .details( ( utuare.isEmergency() ? "EMERGENCY" : "" ) ) + .from( utuare.getSourceID() ) + .to( utuare.getWACN() + "-" + + utuare.getSystemID() + "-" + + utuare.getTargetAddress() ) + .build() ); + } + } + @Override public void setSquelchState( SquelchState state ) { @@ -483,4 +1506,78 @@ public class P25ChannelState extends ChannelState { return mToAlias; } + + public class ActiveCall + { + public static final long TIMEOUT = 2000; //milliseconds + + private CallEvent mEvent; + private long mLastUpdate; + private ThreadPoolManager mThreadPoolManager; + + public ActiveCall( ThreadPoolManager threadManager, CallEvent event ) + { + mThreadPoolManager = threadManager; + mEvent = event; + mLastUpdate = System.currentTimeMillis(); + + mThreadPoolManager.scheduleOnce( new CallExpirationCheck( this ), + TIMEOUT, TimeUnit.MILLISECONDS ); + } + + public CallEvent getCallEvent() + { + return mEvent; + } + + public boolean matches( String channel, String id ) + { + return mEvent.getChannel().contentEquals( channel ) && + mEvent.getToID().contentEquals( id ); + } + + public void update( long timestamp ) + { + mLastUpdate = System.currentTimeMillis(); + } + + public void checkExpiration() + { + long now = System.currentTimeMillis(); + + if( now > mLastUpdate + TIMEOUT ) + { + mCallEventModel.setEnd( mEvent ); + mActiveCalls.remove( this ); + } + else + { + mThreadPoolManager.scheduleOnce( new CallExpirationCheck( this ), + TIMEOUT, TimeUnit.MILLISECONDS ); + } + } + + public long getExpiration() + { + return mLastUpdate + 2000; //2 seconds + } + } + + /** + * Timer to check active calls for call end + */ + public class CallExpirationCheck implements Runnable + { + private ActiveCall mActiveCall; + + public CallExpirationCheck( ActiveCall activeCall ) + { + mActiveCall = activeCall; + } + @Override + public void run() + { + mActiveCall.checkExpiration(); + } + } } diff --git a/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java b/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java index ec747986..aa3ae249 100644 --- a/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java +++ b/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java @@ -30,10 +30,10 @@ public abstract class UnitToUnitChannelGrantExtended extends PDUMessage public static final int[] TARGET_ADDRESS = { 216,217,218,219,220,221,222, 223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239 }; public static final int[] TRANSMIT_IDENTIFIER = { 240,241,242,243 }; - public static final int[] TRANSMIT_CHANNEL = { 244,245,246,247,248,249,250, + public static final int[] TRANSMIT_NUMBER = { 244,245,246,247,248,249,250, 251,252,253,254,255 }; public static final int[] RECEIVE_IDENTIFIER = { 256,257,258,259 }; - public static final int[] RECEIVE_CHANNEL = { 260,261,262,263,264,265,266, + public static final int[] RECEIVE_NUMBER = { 260,261,262,263,264,265,266, 267,268,269,270,271 }; public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, @@ -78,10 +78,10 @@ public abstract class UnitToUnitChannelGrantExtended extends PDUMessage sb.append( " SYS:" ); sb.append( getSourceSystemID() ); - sb.append( " CHAN DN:" + getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); - sb.append( " CHAN UP:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getUplinkFrequency() ); return sb.toString(); @@ -133,45 +133,55 @@ public abstract class UnitToUnitChannelGrantExtended extends PDUMessage return mMessage.getHex( TARGET_ADDRESS, 6 ); } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); + return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); } @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mTransmitIdentifierUpdate = message; } - if( identifier == getReceiveIdentifier() ) + if( identifier == getReceiveChannelIdentifier() ) { mReceiveIdentifierUpdate = message; } @@ -181,8 +191,8 @@ public abstract class UnitToUnitChannelGrantExtended extends PDUMessage { int[] idens = new int[ 2 ]; - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); + idens[ 0 ] = getTransmitChannelIdentifier(); + idens[ 1 ] = getReceiveChannelIdentifier(); return idens; } diff --git a/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java b/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java index b507681e..36a5e425 100644 --- a/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java +++ b/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java @@ -54,7 +54,7 @@ public class GroupAffiliationResponseExtended extends PDUMessage sb.append( getMessageStub() ); sb.append( "AFFILIATION:" ); - sb.append( getGroupAffiliationResponse().name() ); + sb.append( getResponse().name() ); sb.append( " TO:" ); sb.append( getTargetAddress() ); @@ -123,7 +123,7 @@ public class GroupAffiliationResponseExtended extends PDUMessage return mMessage.getHex( GROUP_ID, 4 ); } - public Response getGroupAffiliationResponse() + public Response getResponse() { return Response.fromValue( mMessage.getInt( GROUP_AFFILIATION_RESPONSE_VALUE ) ); diff --git a/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java b/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java index d74aa669..ea23fe51 100644 --- a/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java +++ b/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java @@ -9,6 +9,7 @@ import decode.p25.message.tsbk.osp.control.IdentifierUpdateReceiver; import decode.p25.message.tsbk.osp.control.SystemService; import decode.p25.reference.DataUnitID; import decode.p25.reference.Opcode; +import decode.p25.reference.P25NetworkCallsign; public class NetworkStatusBroadcastExtended extends PDUMessage implements IdentifierUpdateReceiver @@ -88,7 +89,13 @@ public class NetworkStatusBroadcastExtended extends PDUMessage public String getWACN() { - return mMessage.getHex( WACN, 6 ); + return mMessage.getHex( WACN, 5 ); + } + + public String getNetworkCallsign() + { + return P25NetworkCallsign.getCallsign( mMessage.getInt( WACN ), + mMessage.getInt( SYSTEM_ID ) ); } public int getTransmitIdentifier() diff --git a/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java b/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java index 610df8cb..f796a282 100644 --- a/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java +++ b/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java @@ -10,7 +10,7 @@ import decode.p25.reference.Response; public class UnitRegistrationResponseExtended extends PDUMessage { - public static final int[] REGISTRATION_DEFAULT_ADDRESS = { 88,89,90,91,92, + public static final int[] ASSIGNED_SOURCE_ADDRESS = { 88,89,90,91,92, 93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; public static final int[] WACN = { 128,129,130,131,132,133,134,135, 136,137,138,139,140,141,142,143,160,161,162,163 }; @@ -48,7 +48,7 @@ public class UnitRegistrationResponseExtended extends PDUMessage sb.append( getMessageStub() ); sb.append( "REGISTRATION:" ); - sb.append( getUnitRegistrationResponse().name() ); + sb.append( getResponse().name() ); sb.append( " SRC ID:" ); sb.append( getSourceID() ); @@ -65,6 +65,11 @@ public class UnitRegistrationResponseExtended extends PDUMessage return sb.toString(); } + public String getAssignedSourceAddress() + { + return mMessage.getHex( ASSIGNED_SOURCE_ADDRESS, 6 ); + } + public String getWACN() { return mMessage.getHex( WACN, 5 ); @@ -85,7 +90,7 @@ public class UnitRegistrationResponseExtended extends PDUMessage return mMessage.getHex( SOURCE_ADDRESS, 6 ); } - public Response getUnitRegistrationResponse() + public Response getResponse() { return Response.fromValue( mMessage.getInt( UNIT_REGISTRATION_RESPONSE_VALUE ) ); diff --git a/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java b/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java index fdf150fc..33b41c9c 100644 --- a/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java +++ b/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java @@ -25,10 +25,10 @@ public class GroupDataChannelGrantExtended extends PDUMessage public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, 135 }; public static final int[] TRANSMIT_IDENTIFIER = { 160,161,162,163 }; - public static final int[] TRANSMIT_CHANNEL = { 164,165,166,167,168,169,170, + public static final int[] TRANSMIT_NUMBER = { 164,165,166,167,168,169,170, 171,172,173,174,175 }; public static final int[] RECEIVE_IDENTIFIER = { 176,177,178,179 }; - public static final int[] RECEIVE_CHANNEL = { 180,181,182,183,184,185,186, + public static final int[] RECEIVE_NUMBER = { 180,181,182,183,184,185,186, 187,188,189,190,191 }; public static final int[] GROUP_ADDRESS = { 192,193,194,195,196,197,198,199, 200,201,202,203,204,205,206,207 }; @@ -72,10 +72,10 @@ public class GroupDataChannelGrantExtended extends PDUMessage sb.append( " TO:" ); sb.append( getGroupAddress() ); - sb.append( " CHAN DN:" + getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); - sb.append( " CHAN UP:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getUplinkFrequency() ); return sb.toString(); @@ -112,45 +112,55 @@ public class GroupDataChannelGrantExtended extends PDUMessage return mMessage.getHex( GROUP_ADDRESS, 4 ); } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); + return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); } @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mTransmitIdentifierUpdate = message; } - if( identifier == getReceiveIdentifier() ) + if( identifier == getReceiveChannelIdentifier() ) { mReceiveIdentifierUpdate = message; } @@ -160,8 +170,8 @@ public class GroupDataChannelGrantExtended extends PDUMessage { int[] idens = new int[ 2 ]; - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); + idens[ 0 ] = getTransmitChannelIdentifier(); + idens[ 1 ] = getReceiveChannelIdentifier(); return idens; } diff --git a/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java b/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java index 1a918f55..4b4decec 100644 --- a/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java +++ b/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java @@ -23,10 +23,10 @@ public class GroupVoiceChannelGrantExplicit extends PDUMessage public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, 135 }; public static final int[] TRANSMIT_IDENTIFIER = { 176,177,178,179 }; - public static final int[] TRANSMIT_CHANNEL = { 180,181,182,183,184,185,186, + public static final int[] TRANSMIT_NUMBER = { 180,181,182,183,184,185,186, 187,188,189,190,191 }; public static final int[] RECEIVE_IDENTIFIER = { 192,193,194,195 }; - public static final int[] RECEIVE_CHANNEL = { 196,197,198,199,200,201,202, + public static final int[] RECEIVE_NUMBER = { 196,197,198,199,200,201,202, 203,204,205,206,207 }; public static final int[] GROUP_ADDRESS = { 208,209,210,211,212,213,214,215, 216,217,218,219,220,221,222,223 }; @@ -70,10 +70,10 @@ public class GroupVoiceChannelGrantExplicit extends PDUMessage sb.append( " GRP:" ); sb.append( getGroupAddress() ); - sb.append( " CHAN DN:" + getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); - sb.append( " CHAN UP:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getUplinkFrequency() ); return sb.toString(); @@ -110,45 +110,55 @@ public class GroupVoiceChannelGrantExplicit extends PDUMessage return mMessage.getHex( GROUP_ADDRESS, 4 ); } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); + return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); } @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mTransmitIdentifierUpdate = message; } - if( identifier == getReceiveIdentifier() ) + if( identifier == getReceiveChannelIdentifier() ) { mReceiveIdentifierUpdate = message; } @@ -158,8 +168,8 @@ public class GroupVoiceChannelGrantExplicit extends PDUMessage { int[] idens = new int[ 2 ]; - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); + idens[ 0 ] = getTransmitChannelIdentifier(); + idens[ 1 ] = getReceiveChannelIdentifier(); return idens; } diff --git a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java b/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java index c95b71de..abbeb96c 100644 --- a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java +++ b/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java @@ -25,10 +25,10 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, 135 }; public static final int[] TRANSMIT_IDENTIFIER = { 160,161,162,163 }; - public static final int[] TRANSMIT_CHANNEL = { 164,165,166,167,168,169,170, + public static final int[] TRANSMIT_NUMBER = { 164,165,166,167,168,169,170, 171,172,173,174,175 }; public static final int[] RECEIVE_IDENTIFIER = { 176,177,178,179 }; - public static final int[] RECEIVE_CHANNEL = { 180,181,182,183,184,185,186, + public static final int[] RECEIVE_NUMBER = { 180,181,182,183,184,185,186, 187,188,189,190,191 }; public static final int[] CALL_TIMER = { 192,193,194,195,196,197,198,199, 200,201,202,203,204,205,206,207 }; @@ -72,10 +72,10 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage sb.append( " CALL TIMER:" ); sb.append( mTimeDurationFormat.format( new Date( getCallTimer() ) ) ); - sb.append( " CHAN DN:" + getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); - sb.append( " CHAN UP:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getUplinkFrequency() ); return sb.toString(); @@ -86,7 +86,7 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage return mMessage.get( EMERGENCY_FLAG ); } - public boolean isEncryptedChannel() + public boolean isEncrypted() { return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); } @@ -117,45 +117,55 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage return timer / 100; } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); + return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); } @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mTransmitIdentifierUpdate = message; } - if( identifier == getReceiveIdentifier() ) + if( identifier == getReceiveChannelIdentifier() ) { mReceiveIdentifierUpdate = message; } @@ -165,8 +175,8 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage { int[] idens = new int[ 2 ]; - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); + idens[ 0 ] = getTransmitChannelIdentifier(); + idens[ 1 ] = getReceiveChannelIdentifier(); return idens; } diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java b/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java index 11485a49..d7706ff8 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java +++ b/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java @@ -95,12 +95,12 @@ public class UnitToUnitAnswerRequestExplicit extends PDUMessage public String getWACN() { - return mMessage.getHex( WACN, 6 ); + return mMessage.getHex( WACN, 5 ); } public String getSourceID() { - return mMessage.getHex( SOURCE_ID, 4 ); + return mMessage.getHex( SOURCE_ID, 6 ); } public String getTargetAddress() diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java b/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java index 6fd0c469..03a94269 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java +++ b/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java @@ -31,10 +31,10 @@ public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage public static final int[] TARGET_ADDRESS = { 216,217,218,219,220,221,222, 223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239 }; public static final int[] TRANSMIT_IDENTIFIER = { 240,241,242,243 }; - public static final int[] TRANSMIT_CHANNEL = { 244,245,246,247,248,249,250, + public static final int[] TRANSMIT_NUMBER = { 244,245,246,247,248,249,250, 251,252,253,254,255 }; public static final int[] RECEIVE_IDENTIFIER = { 256,257,258,259 }; - public static final int[] RECEIVE_CHANNEL = { 260,261,262,263,264,265,266, + public static final int[] RECEIVE_NUMBER = { 260,261,262,263,264,265,266, 267,268,269,270,271 }; public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, @@ -85,10 +85,10 @@ public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage sb.append( " SYS:" ); sb.append( getSourceSystemID() ); - sb.append( " CHAN DN:" + getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); - sb.append( " CHAN UP:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getUplinkFrequency() ); return sb.toString(); @@ -140,45 +140,55 @@ public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage return mMessage.getHex( TARGET_ADDRESS, 6 ); } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); + return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); } @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mTransmitIdentifierUpdate = message; } - if( identifier == getReceiveIdentifier() ) + if( identifier == getReceiveChannelIdentifier() ) { mReceiveIdentifierUpdate = message; } @@ -188,8 +198,8 @@ public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage { int[] idens = new int[ 2 ]; - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); + idens[ 0 ] = getTransmitChannelIdentifier(); + idens[ 1 ] = getReceiveChannelIdentifier(); return idens; } diff --git a/src/decode/p25/message/tsbk/ChannelGrant.java b/src/decode/p25/message/tsbk/ChannelGrant.java index d4da2b8f..75ad811a 100644 --- a/src/decode/p25/message/tsbk/ChannelGrant.java +++ b/src/decode/p25/message/tsbk/ChannelGrant.java @@ -10,7 +10,7 @@ public abstract class ChannelGrant extends ServiceMessage implements IdentifierUpdateReceiver { public static final int[] PRIORITY = { 85,86,87 }; - public static final int[] CHANNEL_ID = { 88,89,90,91 }; + public static final int[] CHANNEL_IDENTIFIER = { 88,89,90,91 }; public static final int[] CHANNEL_NUMBER = { 92,93,94,95,96,97,98,99,100, 101,102,103 }; @@ -33,14 +33,18 @@ public abstract class ChannelGrant extends ServiceMessage public int getChannelIdentifier() { - return mMessage.getInt( CHANNEL_ID ); + return mMessage.getInt( CHANNEL_IDENTIFIER ); } - public int getChannel() + public int getChannelNumber() { return mMessage.getInt( CHANNEL_NUMBER ); } - + + public String getChannel() + { + return getChannelIdentifier() + "-" + getChannelNumber(); + } @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) @@ -59,12 +63,12 @@ public abstract class ChannelGrant extends ServiceMessage public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannel() ); + return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannel() ); + return calculateUplink( mIdentifierUpdate, getChannelNumber() ); } } diff --git a/src/decode/p25/message/tsbk/GroupChannelGrant.java b/src/decode/p25/message/tsbk/GroupChannelGrant.java index cfe94105..59fbfd61 100644 --- a/src/decode/p25/message/tsbk/GroupChannelGrant.java +++ b/src/decode/p25/message/tsbk/GroupChannelGrant.java @@ -48,7 +48,7 @@ public abstract class GroupChannelGrant extends ChannelGrant sb.append( getGroupAddress() ); sb.append( " CHAN:" ); - sb.append( getChannelIdentifier() + "-" + getChannel() ); + sb.append( getChannelIdentifier() + "-" + getChannelNumber() ); sb.append( " DN:" + getDownlinkFrequency() ); diff --git a/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java b/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java index acc8a09a..deb47589 100644 --- a/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java +++ b/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java @@ -7,11 +7,11 @@ import decode.p25.reference.DataUnitID; public abstract class GroupChannelGrantExplicit extends ChannelGrant { - public static final int[] TRANSMIT_CHANNEL_ID = { 96,97,98,99 }; - public static final int[] TRANSMIT_CHANNEL_NUMBER = { 100,101,102,103, + public static final int[] TRANSMIT_IDENTIFIER = { 96,97,98,99 }; + public static final int[] TRANSMIT_NUMBER = { 100,101,102,103, 104,105,106,107,108,109,110,111 }; - public static final int[] RECEIVE_CHANNEL_ID = { 112,113,114,115 }; - public static final int[] RECEIVE_CHANNEL_NUMBER = { 116,117,118,119, + public static final int[] RECEIVE_IDENTIFIER = { 112,113,114,115 }; + public static final int[] RECEIVE_NUMBER = { 116,117,118,119, 120,121,122,123,124,125,126,127 }; public static final int[] GROUP_ADDRESS = { 128,129,130,131,132,133,134,135, 136,137,138,139,140,141,142,143 }; @@ -38,10 +38,10 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant } sb.append( " XMIT:" ); - sb.append( getTransmitChannelID() + "/" + getTransmitChannelNumber() ); + sb.append( getTransmitChannelIdentifier() + "/" + getTransmitChannelNumber() ); sb.append( " RCV:" ); - sb.append( getReceiveChannelID() + "/" + getReceiveChannelNumber() ); + sb.append( getReceiveChannelIdentifier() + "/" + getReceiveChannelNumber() ); sb.append( " GRP:" ); sb.append( getGroupAddress() ); @@ -49,24 +49,34 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant return sb.toString(); } - public int getTransmitChannelID() + public int getTransmitChannelIdentifier() { - return mMessage.getInt( TRANSMIT_CHANNEL_ID ); + return mMessage.getInt( TRANSMIT_IDENTIFIER ); } public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL_NUMBER ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveChannelID() + public String getTransmitChannel() { - return mMessage.getInt( RECEIVE_CHANNEL_ID ); + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt( RECEIVE_IDENTIFIER ); } public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL_NUMBER ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public String getGroupAddress() @@ -77,12 +87,12 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitChannelID() ) + if( identifier == getTransmitChannelIdentifier() ) { mTransmitIdentifierUpdate = message; } - if( identifier == getReceiveChannelID() ) + if( identifier == getReceiveChannelIdentifier() ) { mReceiveIdentifierUpdate = message; } @@ -92,19 +102,19 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant public int[] getIdentifiers() { int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getTransmitChannelID(); - identifiers[ 1 ] = getReceiveChannelID(); + identifiers[ 0 ] = getTransmitChannelIdentifier(); + identifiers[ 1 ] = getReceiveChannelIdentifier(); return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getChannel() ); + return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getChannel() ); + return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); } } diff --git a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java b/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java index b8084b04..c70cd54f 100644 --- a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java +++ b/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java @@ -9,16 +9,17 @@ import decode.p25.reference.DataUnitID; public abstract class GroupMultiChannelGrant extends ChannelGrant implements IdentifierUpdateReceiver { - public static final int[] CHANNEL_ID_1 = { 80,81,82,83 }; + public static final int[] CHANNEL_IDENTIFIER_1 = { 80,81,82,83 }; public static final int[] CHANNEL_NUMBER_1 = { 84,85,86,87, 88,89,90,91,92,93,94,95 }; - public static final int[] GROUP_ADDRESS_1 = { 96,97,98,99,100,101,102,103 }; + public static final int[] GROUP_ADDRESS_1 = { 96,97,98,99,100,101,102,103, + 104,105,106,107,108,109,110,111 }; - public static final int[] CHANNEL_ID_2 = { 104,105,106,107 }; - public static final int[] CHANNEL_NUMBER_2 = { 108,109,110,111, - 112,113,114,115,116,117,118,119 }; - public static final int[] GROUP_ADDRESS_2 = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135 }; + public static final int[] CHANNEL_IDENTIFIER_2 = { 112,113,114,115 }; + public static final int[] CHANNEL_NUMBER_2 = { 116,117,118,119,120,121,122, + 123,124,125,126,127 }; + public static final int[] GROUP_ADDRESS_2 = { 128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143 }; private IdentifierUpdate mIdentifierUpdate1; private IdentifierUpdate mIdentifierUpdate2; @@ -37,7 +38,7 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant sb.append( getMessageStub() ); sb.append( " CHAN1:" ); - sb.append( getChannelID1() + "/" + getChannelNumber1() ); + sb.append( getChannelIdentifier1() + "/" + getChannelNumber1() ); sb.append( " GRP1:" ); sb.append( getGroupAddress1() ); @@ -45,7 +46,7 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant if( hasChannelNumber2() ) { sb.append( " CHAN2:" ); - sb.append( getChannelID2() + "/" + getChannelNumber2() ); + sb.append( getChannelIdentifier2() + "/" + getChannelNumber2() ); sb.append( " GRP2:" ); sb.append( getGroupAddress2() ); @@ -54,9 +55,9 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant return sb.toString(); } - public int getChannelID1() + public int getChannelIdentifier1() { - return mMessage.getInt( CHANNEL_ID_1 ); + return mMessage.getInt( CHANNEL_IDENTIFIER_1 ); } public int getChannelNumber1() @@ -64,14 +65,19 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant return mMessage.getInt( CHANNEL_NUMBER_1 ); } + public String getChannel1() + { + return getChannelIdentifier1() + "-" + getChannelNumber1(); + } + public String getGroupAddress1() { return mMessage.getHex( GROUP_ADDRESS_1, 4 ); } - public int getChannelID2() + public int getChannelIdentifier2() { - return mMessage.getInt( CHANNEL_ID_2 ); + return mMessage.getInt( CHANNEL_IDENTIFIER_2 ); } public int getChannelNumber2() @@ -79,6 +85,11 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant return mMessage.getInt( CHANNEL_NUMBER_2 ); } + public String getChannel2() + { + return getChannelIdentifier2() + "-" + getChannelNumber2(); + } + public String getGroupAddress2() { return mMessage.getHex( GROUP_ADDRESS_2, 4 ); @@ -86,7 +97,8 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant public boolean hasChannelNumber2() { - return mMessage.getInt( GROUP_ADDRESS_2 ) > 0; + return mMessage.getInt( CHANNEL_NUMBER_2 ) != + mMessage.getInt( CHANNEL_NUMBER_1 ); } @Override @@ -104,12 +116,12 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getChannelID1() ) + if( identifier == getChannelIdentifier1() ) { mIdentifierUpdate1 = message; } - if( hasChannelNumber2() && identifier == getChannelID2() ) + if( hasChannelNumber2() && identifier == getChannelIdentifier2() ) { mIdentifierUpdate2 = message; } @@ -123,13 +135,13 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant if( hasChannelNumber2() ) { identifiers = new int[ 2 ]; - identifiers[ 0 ] = getChannelID1(); - identifiers[ 1 ] = getChannelID2(); + identifiers[ 0 ] = getChannelIdentifier1(); + identifiers[ 1 ] = getChannelIdentifier2(); } else { identifiers = new int[ 1 ]; - identifiers[ 0 ] = getChannelID1(); + identifiers[ 0 ] = getChannelIdentifier1(); } return identifiers; @@ -137,22 +149,22 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant public long getDownlinkFrequency1() { - return calculateDownlink( mIdentifierUpdate1, getChannel() ); + return calculateDownlink( mIdentifierUpdate1, getChannelNumber() ); } public long getUplinkFrequency1() { - return calculateUplink( mIdentifierUpdate1, getChannel() ); + return calculateUplink( mIdentifierUpdate1, getChannelNumber() ); } public long getDownlinkFrequency2() { - return calculateDownlink( mIdentifierUpdate2, getChannel() ); + return calculateDownlink( mIdentifierUpdate2, getChannelNumber() ); } public long getUplinkFrequency2() { - return calculateUplink( mIdentifierUpdate2, getChannel() ); + return calculateUplink( mIdentifierUpdate2, getChannelNumber() ); } } diff --git a/src/decode/p25/message/tsbk/osp/control/DenyResponse.java b/src/decode/p25/message/tsbk/osp/control/DenyResponse.java index 3f8927a1..36e96bab 100644 --- a/src/decode/p25/message/tsbk/osp/control/DenyResponse.java +++ b/src/decode/p25/message/tsbk/osp/control/DenyResponse.java @@ -13,7 +13,10 @@ public class DenyResponse extends TSBKMessage public static final int[] SERVICE_TYPE = { 82,83,84,85,86,87 }; public static final int[] REASON_CODE = { 88,89,90,91,92,93,94,95 }; - /* Additional Info block not yet parsed */ + public static final int[] CALL_OPTIONS = { 96,97,98,99,100,101,102,103 }; + public static final int[] SOURCE_GROUP = { 104,105,106,107,108,109,110,111 }; + public static final int[] SOURCE_ADDRESS = { 96,97,98,99,100,101,102,103,104, + 105,106,107,108,109,110,111 }; public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; @@ -56,9 +59,27 @@ public class DenyResponse extends TSBKMessage return mMessage.get( ADDITIONAL_INFORMATION_FLAG ); } - public String getServiceType() + public Opcode getServiceType() { - return mMessage.getHex( SERVICE_TYPE, 2 ); + return Opcode.fromValue( mMessage.getInt( SERVICE_TYPE ) ); + } + + public String getSourceAddress() + { + switch( getServiceType() ) + { + case GROUP_DATA_CHANNEL_GRANT: + case GROUP_VOICE_CHANNEL_GRANT: + return mMessage.getHex( SOURCE_GROUP, 4 ); + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + case INDIVIDUAL_DATA_CHANNEL_GRANT: + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + default: + break; + } + + return null; } public String getTargetAddress() diff --git a/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java b/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java index 644543d7..cb85e5a7 100644 --- a/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java +++ b/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java @@ -4,13 +4,15 @@ import alias.AliasList; import bits.BitSetBuffer; import decode.p25.message.tsbk.TSBKMessage; import decode.p25.reference.DataUnitID; +import decode.p25.reference.ExtendedFunction; import decode.p25.reference.Opcode; public class ExtendedFunctionCommand extends TSBKMessage { public static final int[] EXTENDED_FUNCTION = { 80,81,82,83,84,85,86,87, - 88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107, - 108,109,110,111,112,113,114,115,116,117,118,119 }; + 88,89,90,91,92,93,94,95 }; + public static final int[] ARGUMENT = { 96,97,98,99,100,101,102,103,104,105, + 106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; @@ -33,16 +35,43 @@ public class ExtendedFunctionCommand extends TSBKMessage sb.append( getMessageStub() ); - sb.append( " EXTENDED FUNCTION:" + getExtendedFunction() ); + sb.append( " EXTENDED FUNCTION:" + getExtendedFunction().getLabel() ); + + sb.append( " ARGUMENT: " + getTargetAddress() ); sb.append( " TGT ADDR: " + getTargetAddress() ); return sb.toString(); } - public String getExtendedFunction() + public ExtendedFunction getExtendedFunction() { - return mMessage.getHex( EXTENDED_FUNCTION, 10 ); + return ExtendedFunction.fromValue( mMessage.getInt( EXTENDED_FUNCTION ) ); + } + + public String getArgument() + { + return mMessage.getHex( ARGUMENT, 6 ); + } + + public String getSourceAddress() + { + switch( getExtendedFunction() ) + { + case RADIO_CHECK: + case RADIO_CHECK_ACK: + case RADIO_DETACH: + case RADIO_DETACH_ACK: + case RADIO_INHIBIT: + case RADIO_INHIBIT_ACK: + case RADIO_UNINHIBIT: + case RADIO_UNINHIBIT_ACK: + return getArgument(); + default: + break; + } + + return null; } public String getTargetAddress() diff --git a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java b/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java index 1ce31817..d161a578 100644 --- a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java +++ b/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java @@ -45,7 +45,7 @@ public class GroupAffiliationResponse extends TSBKMessage return sb.toString(); } - public String getAffiliation() + public String getAffiliationScope() { return mMessage.get( LOCAL_GLOBAL_AFFILIATION_FLAG) ? " GLOBAL" : " LOCAL"; } diff --git a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java b/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java index ab2ac3c6..3445cb01 100644 --- a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java +++ b/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java @@ -5,6 +5,7 @@ import bits.BitSetBuffer; import decode.p25.message.tsbk.TSBKMessage; import decode.p25.reference.DataUnitID; import decode.p25.reference.Opcode; +import decode.p25.reference.P25NetworkCallsign; public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierUpdateReceiver { @@ -74,6 +75,12 @@ public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierUpd return mMessage.getHex( SYSTEM_ID, 3 ); } + public String getNetworkCallsign() + { + return P25NetworkCallsign.getCallsign( mMessage.getInt( WACN ), + mMessage.getInt( SYSTEM_ID ) ); + } + public int getIdentifier() { return mMessage.getInt( IDENTIFIER ); diff --git a/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java b/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java index 0b1b372e..cb24ee51 100644 --- a/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java +++ b/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java @@ -13,7 +13,10 @@ public class QueuedResponse extends TSBKMessage public static final int[] SERVICE_TYPE = { 82,83,84,85,86,87 }; public static final int[] REASON_CODE = { 88,89,90,91,92,93,94,95 }; - /* Additional Info block not yet parsed */ + public static final int[] CALL_OPTIONS = { 96,97,98,99,100,101,102,103 }; + public static final int[] SOURCE_GROUP = { 104,105,106,107,108,109,110,111 }; + public static final int[] SOURCE_ADDRESS = { 96,97,98,99,100,101,102,103,104, + 105,106,107,108,109,110,111 }; public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; @@ -56,9 +59,28 @@ public class QueuedResponse extends TSBKMessage return mMessage.get( ADDITIONAL_INFORMATION_FLAG ); } - public String getServiceType() + public Opcode getServiceType() { - return mMessage.getHex( SERVICE_TYPE, 2 ); + return Opcode.fromValue( mMessage.getInt( SERVICE_TYPE ) ); + } + + public String getSourceAddress() + { + switch( getServiceType() ) + { + case GROUP_DATA_CHANNEL_GRANT: + case GROUP_VOICE_CHANNEL_GRANT: + return mMessage.getHex( SOURCE_GROUP, 4 ); + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + case INDIVIDUAL_DATA_CHANNEL_GRANT: + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + return "DELAY:" + ( mMessage.getInt( SOURCE_ADDRESS ) * 5 ) + "SECS"; + default: + break; + } + + return null; } public String getTargetAddress() diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java b/src/decode/p25/message/tsbk/osp/data/SNDCPData.java index 6a30607b..6e1b54cf 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java +++ b/src/decode/p25/message/tsbk/osp/data/SNDCPData.java @@ -21,4 +21,9 @@ public abstract class SNDCPData extends ServiceMessage { super( message, duid, aliasList ); } + + public String getNSAPI() + { + return mMessage.getHex( NETWORK_SERVICE_ACCESS_POINT_ID, 1 ); + } } diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java index 9c4b0777..35972a24 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java +++ b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java @@ -13,10 +13,10 @@ public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData public static final int REQUESTED_ACCESS_AVAILABLE_INDICATOR = 89; public static final int[] TRANSMIT_IDENTIFIER = { 96,97,98,99 }; - public static final int[] TRANSMIT_CHANNEL = { 100,101,102,103,104,105,106, + public static final int[] TRANSMIT_NUMBER = { 100,101,102,103,104,105,106, 107,108,109,110,111 }; public static final int[] RECEIVE_IDENTIFIER = { 112,113,114,115 }; - public static final int[] RECEIVE_CHANNEL = { 116,117,118,119,120,121,122, + public static final int[] RECEIVE_NUMBER = { 116,117,118,119,120,121,122, 123,124,125,126,127 }; public static final int[] DATA_ACCESS_CONTROL = { 128,129,130,131,132,133, @@ -51,12 +51,12 @@ public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData if( isRequestedAccessAvailable() ) { sb.append( " DOWNLINK:" ); - sb.append( getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getUplinkFrequency() ); sb.append( " UPLINK:" ); - sb.append( getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); @@ -82,24 +82,34 @@ public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData return mMessage.get( REQUESTED_ACCESS_AVAILABLE_INDICATOR ); } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public String getDataAccessControl() @@ -110,11 +120,11 @@ public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mIdentifierUpdateTransmit = message; } - else if( identifier == getReceiveIdentifier() ) + else if( identifier == getReceiveChannelIdentifier() ) { mIdentifierUpdateReceive = message; } @@ -130,19 +140,19 @@ public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData { int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); + identifiers[ 0 ] = getTransmitChannelIdentifier(); + identifiers[ 1 ] = getReceiveChannelIdentifier(); return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdateTransmit, getReceiveChannel() ); + return calculateDownlink( mIdentifierUpdateTransmit, getReceiveChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdateReceive, getTransmitChannel() ); + return calculateUplink( mIdentifierUpdateReceive, getTransmitChannelNumber() ); } } diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java index ae398a4f..4d15b678 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java +++ b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java @@ -9,10 +9,10 @@ import decode.p25.reference.DataUnitID; public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierUpdateReceiver { public static final int[] TRANSMIT_IDENTIFIER = { 88,89,90,91 }; - public static final int[] TRANSMIT_CHANNEL = { 92,93,94,95,96,97,98,99,100, + public static final int[] TRANSMIT_NUMBER = { 92,93,94,95,96,97,98,99,100, 101,102,103 }; public static final int[] RECEIVE_IDENTIFIER = { 104,105,106,107 }; - public static final int[] RECEIVE_CHANNEL = { 108,109,110,111,112,113,114, + public static final int[] RECEIVE_NUMBER = { 108,109,110,111,112,113,114, 115,116,117,118,119 }; public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, @@ -40,12 +40,12 @@ public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierUpdate } sb.append( " UPLINK:" ); - sb.append( getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append( getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); sb.append( " " + getUplinkFrequency() ); sb.append( " DOWNLINK:" ); - sb.append( getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append( getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); sb.append( " " + getDownlinkFrequency() ); @@ -55,26 +55,36 @@ public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierUpdate return sb.toString(); } - public int getTransmitIdentifier() + public int getTransmitChannelIdentifier() { return mMessage.getInt( TRANSMIT_IDENTIFIER ); } - public int getTransmitChannel() + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); + return mMessage.getInt( TRANSMIT_NUMBER ); } - public int getReceiveIdentifier() + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() { return mMessage.getInt( RECEIVE_IDENTIFIER ); } - public int getReceiveChannel() + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_CHANNEL ); + return mMessage.getInt( RECEIVE_NUMBER ); } + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + public String getTargetAddress() { return mMessage.getHex( TARGET_ADDRESS, 6 ); @@ -83,11 +93,11 @@ public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierUpdate @Override public void setIdentifierMessage( int identifier, IdentifierUpdate message ) { - if( identifier == getTransmitIdentifier() ) + if( identifier == getTransmitChannelIdentifier() ) { mIdentifierUpdateTransmit = message; } - else if( identifier == getReceiveIdentifier() ) + else if( identifier == getReceiveChannelIdentifier() ) { mIdentifierUpdateReceive = message; } @@ -103,21 +113,19 @@ public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierUpdate { int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); + identifiers[ 0 ] = getTransmitChannelIdentifier(); + identifiers[ 1 ] = getReceiveChannelIdentifier(); return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdateReceive, getReceiveChannel() ); + return calculateDownlink( mIdentifierUpdateTransmit, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdateTransmit, getTransmitChannel() ); + return calculateUplink( mIdentifierUpdateReceive, getReceiveChannelNumber() ); } - - } diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java b/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java index 2e05cbe6..b4156c09 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java +++ b/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java @@ -29,8 +29,10 @@ public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant sb.append( " EMERGENCY" ); } + sb.append( " ADDR:" + getSourceAddress() ); + sb.append( " CHAN:" ); - sb.append( getChannelIdentifier() + "/" + getChannel() ); + sb.append( getChannel() ); sb.append( " CALL TIMER:" ); sb.append( getCallTimer() ); @@ -41,6 +43,11 @@ public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant return sb.toString(); } + public String getAddress() + { + return getSourceAddress(); + } + /** * Call timer in seconds */ diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java b/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java index 930149b0..d3335d47 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java +++ b/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java @@ -39,7 +39,7 @@ public class UnitToUnitVoiceChannelGrant extends UnitChannelGrant sb.append( " TARGET UNIT:" ); sb.append( getTargetAddress() ); - sb.append( " CHAN:" + getChannelIdentifier() + "-" + getChannel() ); + sb.append( " CHAN:" + getChannelIdentifier() + "-" + getChannelNumber() ); sb.append( " DN:" + getDownlinkFrequency() ); sb.append( " UP:" + getUplinkFrequency() ); diff --git a/src/decode/p25/reference/Encryption.java b/src/decode/p25/reference/Encryption.java index 0eb3e082..6a95ef7a 100644 --- a/src/decode/p25/reference/Encryption.java +++ b/src/decode/p25/reference/Encryption.java @@ -13,6 +13,7 @@ public enum Encryption TRIPLE_DES_2_KEY( 0x82 ), TRIPLE_DES_3_KEY( 0x83 ), AES( 0x84 ), + ADP( 0xAA ), UNKNOWN( -1 ); private int mValue; diff --git a/src/decode/p25/reference/ExtendedFunction.java b/src/decode/p25/reference/ExtendedFunction.java new file mode 100644 index 00000000..3fe9548a --- /dev/null +++ b/src/decode/p25/reference/ExtendedFunction.java @@ -0,0 +1,71 @@ +package decode.p25.reference; + +public enum ExtendedFunction +{ + RADIO_CHECK( 0x0000, "RADIO CHECK" ), + RADIO_DETACH( 0x007D, "RADIO DETACH" ), + RADIO_UNINHIBIT( 0x007E, "RADIO UNINHIBIT" ), + RADIO_INHIBIT( 0x007F, "RADIO INHIBIT" ), + RADIO_CHECK_ACK( 0x0080, "RADIO CHECK ACK" ), + RADIO_DETACH_ACK( 0x00FD, "RADIO DETACH ACK" ), + RADIO_UNINHIBIT_ACK( 0x00FE, "RADIO UNINHIBIT ACK" ), + RADIO_INHIBIT_ACK( 0x00FF, "RADIO INHIBIT ACK" ), + + GROUP_CONTROL_COMMAND( 0x0100, "GROUP CONTROL COMMAND" ), + UNIT_DYNAMIC_COMMAND( 0x0200, "UNIT DYNAMIC COMMAND" ), + GROUP_DYNAMIC_COMMAND( 0x0300, "GROUP DYNAMIC COMMAND" ), + + UNKNOWN( -1, "UNKNOWN" ); + + private int mFunction; + private String mLabel; + + private ExtendedFunction( int function, String label ) + { + mFunction = function; + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static ExtendedFunction fromValue( int function ) + { + switch( function ) + { + case 0x0000: + return RADIO_CHECK; + case 0x007D: + return RADIO_DETACH; + case 0x007E: + return RADIO_UNINHIBIT; + case 0x007F: + return RADIO_INHIBIT; + case 0x0080: + return RADIO_CHECK_ACK; + case 0x00FD: + return RADIO_DETACH_ACK; + case 0x00FE: + return RADIO_UNINHIBIT_ACK; + case 0x00FF: + return RADIO_INHIBIT_ACK; + } + + if( ( function & 0x0100 ) == 0x0100 ) + { + return GROUP_CONTROL_COMMAND; + } + if( ( function & 0x0200 ) == 0x0200 ) + { + return UNIT_DYNAMIC_COMMAND; + } + if( ( function & 0x0300 ) == 0x0300 ) + { + return GROUP_DYNAMIC_COMMAND; + } + + return UNKNOWN; + } +} diff --git a/src/decode/p25/reference/P25NetworkCallsign.java b/src/decode/p25/reference/P25NetworkCallsign.java new file mode 100644 index 00000000..0f3b7028 --- /dev/null +++ b/src/decode/p25/reference/P25NetworkCallsign.java @@ -0,0 +1,48 @@ +package decode.p25.reference; + +public class P25NetworkCallsign +{ + public static final String[] RADIX_50 = new String[] + { " ", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "$", ".", "?", "0", "1", + "2", "3", "4", "5", "6", "7", "8", "9" }; + /** + * Decodes the string callsign from which the WACN and System identifiers + * were derived. + * + * Based on code by Eric Carlson at: + * http://www.ericcarlson.net/project-25-callsign.html + * + * @param wacn + * @param systemID + * @return + */ + public static String getCallsign( int wacn, int systemID ) + { + int n1 = wacn / 16; + int n2 = 4096 * ( wacn % 16 ) + systemID; + + StringBuilder sb = new StringBuilder(); + + sb.append( getLetter( (int)( n1 / 1600 ) ) ); + sb.append( getLetter( (int)( ( n1 / 40 ) % 40 ) ) ); + sb.append( getLetter( (int)( n1 % 40 ) ) ); + sb.append( getLetter( (int)( n2 / 1600 ) ) ); + sb.append( getLetter( (int)( ( n2 / 40 ) % 40 ) ) ); + sb.append( getLetter( (int)( n2 % 40 ) ) ); + + return sb.toString(); + } + + private static String getLetter( int value ) + { + if( 0 <= value && value < 40 ) + { + return RADIX_50[ value ]; + } + + return " "; + } +} diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 8ff69422..9261b976 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -95,7 +95,7 @@ public class TunerChannelSource extends ComplexSource Attribute.SAMPLE_RATE, mTuner.getSampleRate() ) ); /* Schedule the decimation task to run 50 times a second */ - mTaskHandle = mThreadPoolManager.schedule( ThreadType.DECIMATION, + mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION, new DecimationProcessor(), 20, TimeUnit.MILLISECONDS ); /* Finally, register to receive samples from the tuner */