From def5f9ed6fce29b3edd6b61c4d1cc0b64255c69e Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Mon, 17 Nov 2014 01:42:07 +0000 Subject: [PATCH] P25 More work in progress --- src/decode/p25/TrellisHalfRate.java | 26 +--- src/decode/p25/message/tsbk/TSBKMessage.java | 54 +++++++-- .../p25/message/tsbk/TSBKMessageFactory.java | 114 ++++++++++++------ .../osp/control/AdjacentStatusBroadcast.java | 29 +---- .../osp/control/GroupAffiliationResponse.java | 13 +- .../control/LocationRegistrationResponse.java | 78 ++++++++++++ .../tsbk/osp/control/RFSSStatusBroadcast.java | 96 +++++++++++++++ .../osp/control/RadioUnitMonitorCommand.java | 69 +++++++++++ .../osp/control/RoamingAddressCommand.java | 73 +++++++++++ .../SecondaryControlChannelBroadcast.java | 98 +++++++++++++++ .../message/tsbk/osp/control/StatusQuery.java | 62 ++++++++++ .../tsbk/osp/control/StatusUpdate.java | 76 ++++++++++++ .../osp/control/SystemServiceBroadcast.java | 64 ++++++++++ .../UnitDeregistrationAcknowledge.java | 65 ++++++++++ .../osp/control/UnitRegistrationCommand.java | 62 ++++++++++ .../osp/control/UnitRegistrationResponse.java | 71 +++++++++++ src/decode/p25/reference/Response.java | 20 +++ src/decode/p25/reference/Service.java | 67 ++++++++++ src/decode/p25/reference/StackOperation.java | 20 +++ 19 files changed, 1052 insertions(+), 105 deletions(-) create mode 100644 src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java create mode 100644 src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java create mode 100644 src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java create mode 100644 src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java create mode 100644 src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java create mode 100644 src/decode/p25/message/tsbk/osp/control/StatusQuery.java create mode 100644 src/decode/p25/message/tsbk/osp/control/StatusUpdate.java create mode 100644 src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java create mode 100644 src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java create mode 100644 src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java create mode 100644 src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java create mode 100644 src/decode/p25/reference/Response.java create mode 100644 src/decode/p25/reference/Service.java create mode 100644 src/decode/p25/reference/StackOperation.java diff --git a/src/decode/p25/TrellisHalfRate.java b/src/decode/p25/TrellisHalfRate.java index 7c3c18fc..b9064e1f 100644 --- a/src/decode/p25/TrellisHalfRate.java +++ b/src/decode/p25/TrellisHalfRate.java @@ -450,28 +450,14 @@ public class TrellisHalfRate */ public static void main( String[] args ) { - String original = "0010101110101010001011001100111011001000100000101101001000100000110010111010100100100111100100100010000000001110001110010010110000010110001000101101101001110010001001011100001101011110110110011011"; -// String original = "0010111110111110001000100010001011000000000000001101101010101001110011011001001000101100000011100010001000101100001101010010001000011010011100001101100111001110000101111101010001101010101001111110"; -// String original = "0010001011000000110101111110001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010111110111110110000110101001011110101"; - String witherrors = "0010001011000000110101100110001000100010001100100011001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010111110111110110000110101001011110101"; - //Injected errors: xx x x + String original = "00100110000001111000101111110011111000011110100111100001000100100010001011000000110101111110001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010111110111110110000110101001011110101"; - BitSetBuffer bufferOriginal = new BitSetBuffer( 196 ); - BitSetBuffer bufferWithErrors = new BitSetBuffer( 196 ); + BitSetBuffer bufferOriginal = new BitSetBuffer( 260 ); try { - for( int x = 0; x < 196; x++ ) + for( int x = 0; x < 260; x++ ) { - if( witherrors.substring( x, x + 1 ).contentEquals( "0" ) ) - { - bufferWithErrors.add( false ); - } - else - { - bufferWithErrors.add( true ); - } - if( original.substring( x, x + 1 ).contentEquals( "0" ) ) { bufferOriginal.add( false ); @@ -488,15 +474,11 @@ public class TrellisHalfRate } System.out.println( " ORIGINAL: " + original ); - System.out.println( " WITH ERRORS: " + witherrors ); TrellisHalfRate t = new TrellisHalfRate(); - t.decode( bufferOriginal, 0, 196 ); + t.decode( bufferOriginal, 64, 260 ); System.out.println( "ORIG DECODED: " + bufferOriginal.toString() ); - t.decode( bufferWithErrors, 0, 196 ); - System.out.println( "ERRS DECODED: " + bufferWithErrors.toString() ); - } } diff --git a/src/decode/p25/message/tsbk/TSBKMessage.java b/src/decode/p25/message/tsbk/TSBKMessage.java index 550dfdc5..e6bc36a7 100644 --- a/src/decode/p25/message/tsbk/TSBKMessage.java +++ b/src/decode/p25/message/tsbk/TSBKMessage.java @@ -13,6 +13,20 @@ public class TSBKMessage extends P25Message public static final int ENCRYPTED_FLAG = 65; public static final int[] OPCODE = { 66,67,68,69,70,71 }; public static final int[] VENDOR_ID = { 72,73,74,75,76,77,78,79 }; + + public static final int[] BLOCK1 = { 64,65,66,67,68,69,70,71 }; + public static final int[] BLOCK2 = { 72,73,74,75,76,77,78,79 }; + public static final int[] BLOCK3 = { 80,81,82,83,84,85,86,87 }; + public static final int[] BLOCK4 = { 88,89,90,91,92,93,94,95 }; + public static final int[] BLOCK5 = { 96,97,98,99,100,101,102,103 }; + public static final int[] BLOCK6 = { 104,105,106,107,108,109,110,111 }; + public static final int[] BLOCK7 = { 112,113,114,115,116,117,118,119 }; + public static final int[] BLOCK8 = { 120,121,122,123,124,125,126,127 }; + public static final int[] BLOCK9 = { 128,129,130,131,132,133,134,135 }; + public static final int[] BLOCK10 = { 136,137,138,139,140,141,142,143 }; + public static final int[] BLOCK11 = { 144,145,146,147,148,149,150,151 }; + public static final int[] BLOCK12 = { 152,153,154,155,156,157,158,159 }; + public static final int[] CRC = { 144,145,146,147,148,149,150,151,152,153, 154,155,156,157,158,159 }; @@ -45,27 +59,43 @@ public class TSBKMessage extends P25Message public String getMessage() { StringBuilder sb = new StringBuilder(); + + Vendor vendor = getVendor(); - sb.append( "SYSTEM:" ); + sb.append( " SYSTEM:" ); sb.append( getNAC() ); /* NAC is the system id for TSBK messages */ + sb.append( " " ); + sb.append( getDUID().name() ); sb.append( " VEND:" ); - sb.append( getVendor().getLabel() ); + sb.append( vendor.getLabel() ); if( isEncrypted() ) { sb.append( " ENCRYPTED" ); } - else - { - Opcode opcode = getOpcode(); - - sb.append( " OPCODE:" ); - sb.append( opcode.name() ); - sb.append( " [" ); - sb.append( mMessage.getHex( OPCODE, 2 ) ); - sb.append( "]" ); - } + + sb.append( " " + getMessageHex() ); return sb.toString(); } + + public String getMessageHex() + { + StringBuilder sb = new StringBuilder(); + + sb.append( mMessage.getHex( BLOCK1, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK2, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK3, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK4, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK5, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK6, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK7, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK8, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK9, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK10, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK11, 2 ) + " " ); + sb.append( mMessage.getHex( BLOCK12, 2 ) + " " ); + + return sb.toString(); + } } diff --git a/src/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/decode/p25/message/tsbk/TSBKMessageFactory.java index 9208a06d..634d056f 100644 --- a/src/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -11,9 +11,20 @@ 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.IdentifierUpdate; +import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; import decode.p25.message.tsbk.osp.control.MessageUpdate; import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; +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.SecondaryControlChannelBroadcast; +import decode.p25.message.tsbk.osp.control.StatusQuery; +import decode.p25.message.tsbk.osp.control.StatusUpdate; +import decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; +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; @@ -46,42 +57,6 @@ public class TSBKMessageFactory case STANDARD: switch( Opcode.fromValue( opcode ) ) { - case GROUP_VOICE_CHANNEL_GRANT: - return new GroupVoiceChannelGrant( message, duid, - aliasList ); - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - return new GroupVoiceChannelGrantUpdate( message, duid, - aliasList ); - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - return new GroupVoiceChannelGrantUpdateExplicit( message, - duid, aliasList ); - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - return new TelephoneInterconnectVoiceChannelGrant( - message, duid, aliasList ); - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - return new TelephoneInterconnectAnswerRequest( - message, duid, aliasList ); - case UNIT_TO_UNIT_ANSWER_REQUEST: - return new UnitToUnitAnswerRequest( message, duid, - aliasList ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - return new UnitToUnitVoiceChannelGrant( message, - duid, aliasList ); - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - return new UnitToUnitVoiceChannelGrantUpdate( message, - duid, aliasList ); - case INDIVIDUAL_DATA_CHANNEL_GRANT: - return new IndividualDataChannelGrant( message, duid, - aliasList ); - case GROUP_DATA_CHANNEL_GRANT: - return new GroupDataChannelGrant( message, duid, - aliasList ); - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - return new GroupDataChannelAnnouncement( message, duid, - aliasList ); - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - return new GroupDataChannelAnnouncementExplicit( - message, duid, aliasList ); case ACKNOWLEDGE_RESPONSE_FNE: return new AcknowledgeResponse( message, duid, aliasList ); @@ -104,8 +79,32 @@ public class TSBKMessageFactory case GROUP_AFFILIATION_RESPONSE: return new GroupAffiliationResponse( message, duid, aliasList ); + case GROUP_DATA_CHANNEL_ANNOUNCEMENT: + return new GroupDataChannelAnnouncement( message, duid, + aliasList ); + case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + return new GroupDataChannelAnnouncementExplicit( + message, duid, aliasList ); + case GROUP_DATA_CHANNEL_GRANT: + return new GroupDataChannelGrant( message, duid, + aliasList ); + case GROUP_VOICE_CHANNEL_GRANT: + return new GroupVoiceChannelGrant( message, duid, + aliasList ); + case GROUP_VOICE_CHANNEL_GRANT_UPDATE: + return new GroupVoiceChannelGrantUpdate( message, duid, + aliasList ); + case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + return new GroupVoiceChannelGrantUpdateExplicit( message, + duid, aliasList ); case IDENTIFIER_UPDATE: return new IdentifierUpdate( message, duid, aliasList ); + case INDIVIDUAL_DATA_CHANNEL_GRANT: + return new IndividualDataChannelGrant( message, duid, + aliasList ); + case LOCATION_REGISTRATION_RESPONSE: + return new LocationRegistrationResponse( message, duid, + aliasList ); case MESSAGE_UPDATE: return new MessageUpdate( message, duid, aliasList ); case NETWORK_STATUS_BROADCAST: @@ -114,6 +113,49 @@ public class TSBKMessageFactory case PROTECTION_PARAMETER_UPDATE: return new ProtectionParameterUpdate( message, duid, aliasList ); + case RADIO_UNIT_MONITOR_COMMAND: + return new RadioUnitMonitorCommand( message, duid, + aliasList ); + case RFSS_STATUS_BROADCAST: + return new RFSSStatusBroadcast( message, duid, + aliasList ); + case ROAMING_ADDRESS_COMMAND: + return new RoamingAddressCommand( message, duid, + aliasList ); + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + return new SecondaryControlChannelBroadcast( message, + duid, aliasList ); + case STATUS_QUERY: + return new StatusQuery( message, duid, aliasList ); + case STATUS_UPDATE: + return new StatusUpdate( message, duid, aliasList ); + case SYSTEM_SERVICE_BROADCAST: + return new SystemServiceBroadcast( message, duid, + aliasList ); + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + return new TelephoneInterconnectAnswerRequest( + message, duid, aliasList ); + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + return new TelephoneInterconnectVoiceChannelGrant( + message, duid, aliasList ); + case UNIT_DEREGISTRATION_ACKNOWLEDGE: + return new UnitDeregistrationAcknowledge( message, + duid, aliasList ); + case UNIT_REGISTRATION_COMMAND: + return new UnitRegistrationCommand( message, duid, + aliasList ); + case UNIT_REGISTRATION_RESPONSE: + return new UnitRegistrationResponse( message, duid, + aliasList ); + case UNIT_TO_UNIT_ANSWER_REQUEST: + return new UnitToUnitAnswerRequest( message, duid, + aliasList ); + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + return new UnitToUnitVoiceChannelGrant( message, + duid, aliasList ); + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + return new UnitToUnitVoiceChannelGrantUpdate( message, + duid, aliasList ); default: return new TSBKMessage( message, duid, aliasList ); } diff --git a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java b/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java index ee8fe7e0..2ee521b9 100644 --- a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java +++ b/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java @@ -62,7 +62,7 @@ public class AdjacentStatusBroadcast extends TSBKMessage sb.append( " FAILURE-CONDITION" ); } - if( isValid() ) + if( isValidSiteInformation() ) { sb.append( " VALID-INFO" ); } @@ -126,31 +126,4 @@ public class AdjacentStatusBroadcast extends TSBKMessage { return mMessage.getInt( SYSTEM_SERVICE_CLASS ); } - - public String getSourceAddress() - { - if( hasAdditionalInformation() && !hasExtendedAddress() ) - { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - return null; - } - - @Override - public String getFromID() - { - return getSourceAddress(); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - @Override - public String getToID() - { - return getTargetAddress(); - } } diff --git a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java b/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java index e2821969..b6d70b05 100644 --- a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java +++ b/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java @@ -5,13 +5,12 @@ import bits.BitSetBuffer; import decode.p25.message.tsbk.TSBKMessage; import decode.p25.reference.DataUnitID; import decode.p25.reference.Opcode; +import decode.p25.reference.Response; public class GroupAffiliationResponse extends TSBKMessage { - public enum AffiliationStatus { ACCEPT, FAIL, DENY, REFUSED }; - public static final int LOCAL_GLOBAL_AFFILIATION_FLAG = 80; - public static final int[] GROUP_AFFILIATION_VALUE = { 86,87 }; + public static final int[] AFFILIATION_RESPONSE = { 86,87 }; public static final int[] ANNOUNCEMENT_GROUP_ADDRESS = { 88,89,90,91, 92,93,94,95,96,97,98,99,100,101,102,103 }; public static final int[] GROUP_ADDRESS = { 104,105,106,107,108,109,110,111, @@ -38,7 +37,7 @@ public class GroupAffiliationResponse extends TSBKMessage sb.append( super.getMessage() ); - sb.append( " STATUS:" + getStatus().name() ); + sb.append( " AFFILIATION:" + getResponse().name() ); sb.append( " ANNOUNCE GROUP:" + getAnnouncementGroupAddress() ); sb.append( " GRP ADDR:" + getGroupAddress() ); sb.append( " TGT ADDR: " + getTargetAddress() ); @@ -51,11 +50,11 @@ public class GroupAffiliationResponse extends TSBKMessage return mMessage.get( LOCAL_GLOBAL_AFFILIATION_FLAG) ? " GLOBAL" : " LOCAL"; } - public AffiliationStatus getStatus() + public Response getResponse() { - int status = mMessage.getInt( GROUP_AFFILIATION_VALUE ); + int response = mMessage.getInt( AFFILIATION_RESPONSE ); - return AffiliationStatus.values()[ status ]; + return Response.fromValue( response ); } public String getAnnouncementGroupAddress() diff --git a/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java b/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java new file mode 100644 index 00000000..eacef6b2 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java @@ -0,0 +1,78 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; +import decode.p25.reference.Response; + +public class LocationRegistrationResponse extends TSBKMessage +{ + public static final int[] REGISTRATION_RESPONSE = { 86,87 }; + public static final int[] GROUP_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, + 98,99,100,101,102,103 }; + public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 }; + public static final int[] SITE_ID = { 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 }; + + public LocationRegistrationResponse( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.LOCATION_REGISTRATION_RESPONSE.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " REGISTRATION:" + getResponse().name() ); + sb.append( " GROUP:" + getGroupAddress() ); + sb.append( " RFSS: " + getRFSSID() ); + sb.append( " SITE: " + getSiteID() ); + sb.append( " TGT ADDR: " + getTargetAddress() ); + + return sb.toString(); + } + + public Response getResponse() + { + return Response.fromValue( mMessage.getInt( REGISTRATION_RESPONSE ) ); + } + + public String getGroupAddress() + { + return mMessage.getHex( GROUP_ADDRESS, 6 ); + } + + public String getRFSSID() + { + return mMessage.getHex( RFSS_ID, 2 ); + } + + public String getSiteID() + { + return mMessage.getHex( SITE_ID, 2 ); + } + + public String getTargetAddress() + { + return mMessage.getHex( TARGET_ADDRESS, 6 ); + } + + @Override + public String getToID() + { + return getTargetAddress(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java b/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java new file mode 100644 index 00000000..0910f99a --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java @@ -0,0 +1,96 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; + +public class RFSSStatusBroadcast extends TSBKMessage +{ + public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85, + 86,87 }; + public static final int ACTIVE_NETWORK_CONNECTION_FLAG = 91; + public static final int[] SYSTEM_ID = { 92,93,94,95,96,97,98,99,100,101, + 102,103 }; + public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 }; + public static final int[] SITE_ID = { 112,113,114,115,116,117,118,119 }; + public static final int[] CHANNEL = { 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135 }; + public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141, + 142,143 }; + + public RFSSStatusBroadcast( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.RFSS_STATUS_BROADCAST.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " LRA:" + getLocationRegistrationArea() ); + + sb.append( " SYSID:" + getSystemID() ); + + sb.append( " RFSS:" + getRFSS() ); + + sb.append( " SITE:" + getSiteID() ); + + sb.append( " CHAN:" + getChannel() ); + + if( hasActiveNetworkConnection() ) + { + sb.append( " ACTIVE-NETWORK-CONN" ); + } + + sb.append( SystemService.toString( getSystemServiceClass() ) ); + + return sb.toString(); + } + + public String getLocationRegistrationArea() + { + return mMessage.getHex( LOCATION_REGISTRATION_AREA, 2 ); + } + + public boolean hasActiveNetworkConnection() + { + return mMessage.get( ACTIVE_NETWORK_CONNECTION_FLAG ); + } + + public String getSystemID() + { + return mMessage.getHex( SYSTEM_ID, 3 ); + } + + public String getRFSS() + { + return mMessage.getHex( RFSS_ID, 2 ); + } + + public String getSiteID() + { + return mMessage.getHex( SITE_ID, 2 ); + } + + public int getChannel() + { + return mMessage.getInt( CHANNEL ); + } + + public int getSystemServiceClass() + { + return mMessage.getInt( SYSTEM_SERVICE_CLASS ); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java b/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java new file mode 100644 index 00000000..52885b3f --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java @@ -0,0 +1,69 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; + +public class RadioUnitMonitorCommand extends TSBKMessage +{ + public static final int[] TX_MULTIPLIER = { 94,95 }; + public static final int[] SOURCE_ADDRESS = { 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 }; + + public RadioUnitMonitorCommand( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.RADIO_UNIT_MONITOR_COMMAND.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " MULT:" + getTransmitMultiplier() ); + sb.append( " SRC ADDR: " + getSourceAddress() ); + sb.append( " TGT ADDR: " + getTargetAddress() ); + + return sb.toString(); + } + + public int getTransmitMultiplier() + { + return mMessage.getInt( TX_MULTIPLIER ); + } + + public String getSourceAddress() + { + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + } + + @Override + public String getFromID() + { + return getSourceAddress(); + } + + public String getTargetAddress() + { + return mMessage.getHex( TARGET_ADDRESS, 6 ); + } + + @Override + public String getToID() + { + return getTargetAddress(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java b/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java new file mode 100644 index 00000000..c05016a6 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java @@ -0,0 +1,73 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; +import decode.p25.reference.Response; +import decode.p25.reference.StackOperation; + +public class RoamingAddressCommand extends TSBKMessage +{ + public static final int[] STACK_OPERATION = { 80,81,82,83,84,85,86,87 }; + public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, + 101,102,103,104,105,106,107 }; + public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115,116, + 117,118,119 }; + public static final int[] TARGET_ID = { 120,121,122,123,124,125,126,127,128, + 129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; + + public RoamingAddressCommand( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.ROAMING_ADDRESS_COMMAND.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( getStackOperation().name() ); + sb.append( " WACN:" + getWACN() ); + sb.append( " SYSTEM: " + getSystemID() ); + sb.append( " TGT ID: " + getTargetID() ); + + return sb.toString(); + } + + public StackOperation getStackOperation() + { + return StackOperation.fromValue( mMessage.getInt( STACK_OPERATION ) ); + } + + public String getWACN() + { + return mMessage.getHex( WACN, 5 ); + } + + public String getSystemID() + { + return mMessage.getHex( SYSTEM_ID, 3 ); + } + + public String getTargetID() + { + return mMessage.getHex( TARGET_ID, 6 ); + } + + @Override + public String getToID() + { + return getTargetID(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java new file mode 100644 index 00000000..94fe95c3 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java @@ -0,0 +1,98 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; + +public class SecondaryControlChannelBroadcast extends TSBKMessage +{ + public static final int[] RFSS_ID = { 80,81,82,83,84,85,86,87 }; + public static final int[] SITE_ID = { 88,89,90,91,92,93,94,95 }; + public static final int[] CHANNEL_1 = { 96,97,98,99,100,101,102,103,104,105, + 106,107,108,109,110,111 }; + public static final int[] SYSTEM_SERVICE_CLASS_1 = { 112,113,114,115,116, + 117,118,119 }; + public static final int[] CHANNEL_2 = { 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135 }; + public static final int[] SYSTEM_SERVICE_CLASS_2 = { 136,137,138,139,140, + 141,142,143 }; + + public SecondaryControlChannelBroadcast( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " RFSS:" + getRFSS() ); + + sb.append( " SITE:" + getSiteID() ); + + sb.append( " CHAN 1:" + getChannel1() ); + + sb.append( " SVC CLASS 1:" + + SystemService.toString( getSystemServiceClass1() ) ); + + if( hasChannel2() ) + { + sb.append( " CHAN 2:" + getChannel2() ); + + sb.append( " SVC CLASS 2:" + + SystemService.toString( getSystemServiceClass2() ) ); + } + else + { + sb.append( " CHAN 2: empty" ); + } + return sb.toString(); + } + + public String getRFSS() + { + return mMessage.getHex( RFSS_ID, 2 ); + } + + public String getSiteID() + { + return mMessage.getHex( SITE_ID, 2 ); + } + + public int getChannel1() + { + return mMessage.getInt( CHANNEL_1 ); + } + + public int getSystemServiceClass1() + { + return mMessage.getInt( SYSTEM_SERVICE_CLASS_1 ); + } + + public int getChannel2() + { + return mMessage.getInt( CHANNEL_2 ); + } + + public int getSystemServiceClass2() + { + return mMessage.getInt( SYSTEM_SERVICE_CLASS_2 ); + } + + public boolean hasChannel2() + { + return getSystemServiceClass2() != 0; + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/StatusQuery.java b/src/decode/p25/message/tsbk/osp/control/StatusQuery.java new file mode 100644 index 00000000..2d002e19 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/StatusQuery.java @@ -0,0 +1,62 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; + +public class StatusQuery extends TSBKMessage +{ + public static final int[] TARGET_ADDRESS = { 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[] SOURCE_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 }; + + public StatusQuery( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.STATUS_QUERY.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " SRC ADDR: " + getSourceAddress() ); + sb.append( " TGT ADDR: " + getTargetAddress() ); + + return sb.toString(); + } + + public String getSourceAddress() + { + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + } + + @Override + public String getFromID() + { + return getSourceAddress(); + } + + public String getTargetAddress() + { + return mMessage.getHex( TARGET_ADDRESS, 6 ); + } + + @Override + public String getToID() + { + return getTargetAddress(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java b/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java new file mode 100644 index 00000000..b96d5a87 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java @@ -0,0 +1,76 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; + +public class StatusUpdate extends TSBKMessage +{ + public static final int[] USER_STATUS = { 80,81,82,83,84,85,86,87 }; + public static final int[] UNIT_STATUS = { 88,89,90,91,92,93,94,95 }; + public static final int[] TARGET_ADDRESS = { 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[] SOURCE_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 }; + + public StatusUpdate( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.STATUS_QUERY.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " STATUS USER:" + getUserStatus() ); + sb.append( " UNIT:" + getUnitStatus() ); + sb.append( " SRC ADDR: " + getSourceAddress() ); + sb.append( " TGT ADDR: " + getTargetAddress() ); + + return sb.toString(); + } + + public String getUserStatus() + { + return mMessage.getHex( USER_STATUS, 2 ); + } + + public String getUnitStatus() + { + return mMessage.getHex( UNIT_STATUS, 2 ); + } + + public String getSourceAddress() + { + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + } + + @Override + public String getFromID() + { + return getSourceAddress(); + } + + public String getTargetAddress() + { + return mMessage.getHex( TARGET_ADDRESS, 6 ); + } + + @Override + public String getToID() + { + return getTargetAddress(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java b/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java new file mode 100644 index 00000000..6c48a4de --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java @@ -0,0 +1,64 @@ +package decode.p25.message.tsbk.osp.control; + +import java.util.List; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; +import decode.p25.reference.Service; + +public class SystemServiceBroadcast extends TSBKMessage +{ + public static final int[] AVAILABLE_SERVICES = { 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[] SUPPORTED_SERVICES = { 112,113,114,115,116,117, + 118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135 }; + public static final int[] REQUEST_PRIORITY_LEVEL = { 136,137,138,139,140, + 141,142,143 }; + + public SystemServiceBroadcast( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.SYSTEM_SERVICE_BROADCAST.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " SERVICES AVAILABLE " ); + + sb.append( getAvailableServices() ); + + sb.append( " SUPPORTED " ); + + sb.append( getSupportedServices() ); + + return sb.toString(); + } + + public List getAvailableServices() + { + long bitmap = mMessage.getLong( AVAILABLE_SERVICES ); + + return Service.getServices( bitmap ); + } + + public List getSupportedServices() + { + long bitmap = mMessage.getLong( SUPPORTED_SERVICES ); + + return Service.getServices( bitmap ); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java b/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java new file mode 100644 index 00000000..0402bede --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java @@ -0,0 +1,65 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; +import decode.p25.reference.Response; + +public class UnitDeregistrationAcknowledge extends TSBKMessage +{ + public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, + 101,102,103,104,105,106,107 }; + public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115, + 116,117,118,119 }; + public static final int[] SOURCE_ID = { 120,121,122,123,124,125,126, + 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; + + public UnitDeregistrationAcknowledge( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.UNIT_DEREGISTRATION_ACKNOWLEDGE.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " WACN:" + getWACN() ); + sb.append( " SYSTEM:" + getSystemID() ); + sb.append( " SRC ID: " + getSourceID() ); + + return sb.toString(); + } + + public String getWACN() + { + return mMessage.getHex( WACN, 5 ); + } + + public String getSystemID() + { + return mMessage.getHex( SYSTEM_ID, 3 ); + } + + public String getSourceID() + { + return mMessage.getHex( SOURCE_ID, 6 ); + } + + @Override + public String getToID() + { + return getSourceID(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java b/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java new file mode 100644 index 00000000..6a15cea3 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java @@ -0,0 +1,62 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; + +public class UnitRegistrationCommand extends TSBKMessage +{ + public static final int[] TARGET_ADDRESS = { 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[] SOURCE_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 }; + + public UnitRegistrationCommand( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.UNIT_REGISTRATION_COMMAND.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " SRC ADDR: " + getSourceAddress() ); + sb.append( " TGT ADDR: " + getTargetAddress() ); + + return sb.toString(); + } + + public String getSourceAddress() + { + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + } + + @Override + public String getFromID() + { + return getSourceAddress(); + } + + public String getTargetAddress() + { + return mMessage.getHex( TARGET_ADDRESS, 6 ); + } + + @Override + public String getToID() + { + return getTargetAddress(); + } +} diff --git a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java b/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java new file mode 100644 index 00000000..4cde5e23 --- /dev/null +++ b/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java @@ -0,0 +1,71 @@ +package decode.p25.message.tsbk.osp.control; + +import alias.AliasList; +import bits.BitSetBuffer; +import decode.p25.message.tsbk.TSBKMessage; +import decode.p25.reference.DataUnitID; +import decode.p25.reference.Opcode; +import decode.p25.reference.Response; + +public class UnitRegistrationResponse extends TSBKMessage +{ + public static final int[] REGISTRATION_RESPONSE = { 82,83 }; + public static final int[] SYSTEM_ID = { 84,85,86,87,88,89,90,91,92,93,94,95 }; + public static final int[] SOURCE_ID = { 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[] SOURCE_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 }; + + public UnitRegistrationResponse( BitSetBuffer message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.UNIT_REGISTRATION_RESPONSE.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( super.getMessage() ); + + sb.append( " REGISTRATION:" + getResponse().name() ); + sb.append( " SYSTEM:" + getSystemID() ); + sb.append( " SRC ID: " + getSourceID() ); + sb.append( " SRC ADDR: " + getSourceAddress() ); + + return sb.toString(); + } + + public Response getResponse() + { + return Response.fromValue( mMessage.getInt( REGISTRATION_RESPONSE ) ); + } + + public String getSystemID() + { + return mMessage.getHex( SYSTEM_ID, 3 ); + } + + public String getSourceID() + { + return mMessage.getHex( SOURCE_ID, 6 ); + } + + public String getSourceAddress() + { + return mMessage.getHex( SOURCE_ADDRESS, 6 ); + } + + @Override + public String getToID() + { + return getSourceAddress(); + } +} diff --git a/src/decode/p25/reference/Response.java b/src/decode/p25/reference/Response.java new file mode 100644 index 00000000..4951300f --- /dev/null +++ b/src/decode/p25/reference/Response.java @@ -0,0 +1,20 @@ +package decode.p25.reference; + +public enum Response +{ + ACCEPT, + FAIL, + DENY, + REFUSED, + UNKNOWN; + + public static Response fromValue( int value ) + { + if( 0 <= value && value <= 4 ) + { + return values()[ value ]; + } + + return UNKNOWN; + } +} diff --git a/src/decode/p25/reference/Service.java b/src/decode/p25/reference/Service.java new file mode 100644 index 00000000..fd3baae6 --- /dev/null +++ b/src/decode/p25/reference/Service.java @@ -0,0 +1,67 @@ +package decode.p25.reference; + +import java.util.ArrayList; +import java.util.List; + +public enum Service +{ + EXTENDED_SERVICES( 0x800000l ), + EXTENDED_SERVICES_EXTENSION( 0x400000l ), + + /* NORMAL SERVICES */ + NETWORK_ACTIVE( 0x200000l ), + RESERVED_4( 0x100000l ), + GROUP_VOICE( 0x080000l ), + INDIVIDUAL_VOICE( 0x040000l ), + PSTN_TO_UNIT_VOICE( 0x020000l ), + UNIT_TO_PSTN_VOICE( 0x010000l ), + RESERVED_9( 0x008000l ), + GROUP_DATA( 0x004000l ), + INDIVIDUAL_DATA( 0x002000l ), + RESERVED_12( 0x001000l ), + UNIT_REGISTRATION( 0x000800l ), + GROUP_AFFILIATION( 0x000400l ), + GROUP_AFFILIATION_QUERY( 0x000200l ), + AUTHENTICATION( 0x000100l ), + ENCRYPTION_SETTINGS( 0x000080l ), + USER_STATUS( 0x000040l ), + USER_MESSAGE( 0x000020l ), + UNIT_STATUS( 0x000010l ), + USER_STATUS_QUERY( 0x000008l ), + UNIT_STATUS_QUERY( 0x000004l ), + UNIT_PAGE( 0x000002l ), + EMERGENCY_ALARM( 0x000001l ), + UNKNOWN( 0x0l ); + + private long mCode; + + private Service( long code ) + { + mCode = code; + } + + public long getCode() + { + return mCode; + } + + public static boolean isSupported( Service service, long serviceBitmap ) + { + return ( service.getCode() & serviceBitmap ) == service.getCode(); + } + + public static List getServices( long serviceBitmap ) + { + List services = new ArrayList(); + + for( Service service: values() ) + { + if( isSupported( service, serviceBitmap ) ) + { + services.add( service ); + } + } + + return services; + } +} diff --git a/src/decode/p25/reference/StackOperation.java b/src/decode/p25/reference/StackOperation.java new file mode 100644 index 00000000..6d93356c --- /dev/null +++ b/src/decode/p25/reference/StackOperation.java @@ -0,0 +1,20 @@ +package decode.p25.reference; + +public enum StackOperation +{ + CLEAR, + WRITE, + DELETE, + READ, + UNKNOWN; + + public static StackOperation fromValue( int value ) + { + if( 0 <= value && value <= 3 ) + { + return values()[ value ]; + } + + return UNKNOWN; + } +}