From 5c2ff8318349fa9478623c0bb994cf5bb018259e Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Sat, 6 Sep 2014 11:13:58 +0000 Subject: [PATCH] MPT1327 - added SDM parsing for ASCII, BCD, Binary, TELEX, Map27, etc. --- src/bits/BitSetBuffer.java | 86 +- src/decode/mpt1327/MPT1327ChannelState.java | 12 + src/decode/mpt1327/MPT1327Message.java | 1035 +++++++++++++++++-- 3 files changed, 1045 insertions(+), 88 deletions(-) diff --git a/src/bits/BitSetBuffer.java b/src/bits/BitSetBuffer.java index d72c23f9..f690c21d 100644 --- a/src/bits/BitSetBuffer.java +++ b/src/bits/BitSetBuffer.java @@ -251,6 +251,13 @@ public class BitSetBuffer extends BitSet */ public int getInt( int[] bits ) { + if( bits.length > 31 ) + { + throw new IllegalArgumentException( "BitSetBuffer getInt() " + + "array length must be less than 32 or the value exceeds " + + "integer size" ); + } + int retVal = 0; for( int x = 0; x < bits.length; x++ ) @@ -264,11 +271,86 @@ public class BitSetBuffer extends BitSet return retVal; } + /** + * Returns the integer value represented by the bit array + * @param bits - an array of bit positions that will be treated as if they + * were contiguous bits, with index 0 being the MSB and index + * length - 1 being the LSB + * @return - integer value of the bit array + */ + public long getLong( int[] bits ) + { + if( bits.length > 63 ) + { + throw new IllegalArgumentException( "BitSetBuffer getLong() " + + "array length must be less than 64 or the value exceeds " + + "a primitive long size" ); + } + + long retVal = 0; + + for( int x = 0; x < bits.length; x++ ) + { + if( get( bits[ x ] ) ) + { + retVal += 1<<( bits.length - 1 - x ); + } + } + + return retVal; + } + + /** + * Converts up to 63 bits from the bit array into an integer and then + * formats the value into hexadecimal, prefixing the value with zeros to + * provide a total length of digitDisplayCount; + * + * @param bits + * @param digitDisplayCount + * @return + */ public String getHex( int[] bits, int digitDisplayCount ) { - int value = getInt( bits ); + if( bits.length <= 31 ) + { + int value = getInt( bits ); + + return String.format( "%0" + digitDisplayCount + "X", value ); + } + else if( bits.length <= 63 ) + { + long value = getLong( bits ); + + return String.format( "%0" + digitDisplayCount + "X", value ); + } + else + { + throw new IllegalArgumentException( "BitSetBuffer.getHex() " + + "maximum array length is 63 bits" ); + } + } + + public String getHex( int msb, int lsb, int digitDisplayCount ) + { + int length = lsb - msb; - return String.format( "%0" + digitDisplayCount + "X", value ); + if( length <= 31 ) + { + int value = getInt( msb, lsb ); + + return String.format( "%0" + digitDisplayCount + "X", value ); + } + else if( length <= 63 ) + { + long value = getLong( msb, lsb ); + + return String.format( "%0" + digitDisplayCount + "X", value ); + } + else + { + throw new IllegalArgumentException( "BitSetBuffer.getHex() " + + "maximum array length is 63 bits" ); + } } /** diff --git a/src/decode/mpt1327/MPT1327ChannelState.java b/src/decode/mpt1327/MPT1327ChannelState.java index db206efa..a0c83617 100644 --- a/src/decode/mpt1327/MPT1327ChannelState.java +++ b/src/decode/mpt1327/MPT1327ChannelState.java @@ -230,6 +230,18 @@ public class MPT1327ChannelState extends ChannelState fade( CallEventType.CALL_END ); } break; + case HEAD_PLUS1: + case HEAD_PLUS2: + case HEAD_PLUS3: + case HEAD_PLUS4: + mCallEventModel.add( + new MPT1327CallEvent.Builder( CallEventType.SDM ) + .aliasList( mAliasList ) + .details( mpt.getMessageType().getDescription() ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + break; } } } diff --git a/src/decode/mpt1327/MPT1327Message.java b/src/decode/mpt1327/MPT1327Message.java index 7684fdcb..881e4a77 100644 --- a/src/decode/mpt1327/MPT1327Message.java +++ b/src/decode/mpt1327/MPT1327Message.java @@ -19,6 +19,7 @@ package decode.mpt1327; import java.text.DecimalFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.BitSet; import java.util.Calendar; import java.util.Date; @@ -34,44 +35,93 @@ import crc.CRCFleetsync; public class MPT1327Message extends Message { - private static DecimalFormat mDecimalFormatter = new DecimalFormat("#.#####"); - private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); + private static String[] TELEX_LETTERS = + { "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","\n","\n","",""," "," " }; + + private static String[] TELEX_FIGURES = + { "-","?",":","WRU","3","{6}","{7}","{8}", + "8","{BEEP}","(",")",".",",","9","0", + "1","4","'","5","7","=","2","/", + "6","+","\n","\n","",""," "," " }; - //Calendar to use in calculating time hacks - Calendar mCalendar = new GregorianCalendar(); + private static String[] BCD = { "0","1","2","3","4","5","6", + "7","8","9"," ","*","#" }; - //Message parts are identified in big-endian order for correct translation private static int BLOCK_1_START = 20; private static int BLOCK_2_START = 84; private static int BLOCK_3_START = 148; private static int BLOCK_4_START = 212; private static int BLOCK_5_START = 276; - private static int BLOCK_5_END = 340; + private static int BLOCK_6_START = 340; private static int[] REVS = { 0,1,2,3 }; private static int[] SYNC = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }; /* Block 1 Fields */ - private static int[] B1_PREFIX = { 21,22,23,24,25,26,27 }; - private static int[] B1_IDENT1 = { 28,29,30,31,32,33,34,35,36,37,38,39,40 }; - private static int[] B1_IDENT2_GTC = { 53,54,55,56,57,58,59,60,61,62,63,64,65 }; - private static int[] B1_IDENT2 = { 50,51,52,53,54,55,56,57,58,59,60,61,62 }; - private static int[] B1_GTC_CHAN = { 43,44,45,46,47,48,49,50,51,52 }; - private static int[] B1_MESSAGE_TYPE = { 41,42,43,44,45,46,47,48,49 }; - private static int[] B1_STATUS_MESSAGE = { 66,67,68,69,70 }; - private static int[] B1_SYSTEM_ID = { 26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 }; private static int[] B1_SYSDEF = { 21,22,23,24,25 }; - private static int[] B1_CHANNEL = { 35,36,37,38,39,40,41,42,43,44 }; + private static int[] B1_PREFIX = { 21,22,23,24,25,26,27 }; private static int[] B1_TRAFFIC_CHANNEL = { 21,22,23,24,25,26,27,28,29,30 }; + private static int[] B1_SYSTEM_ID = { 26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 }; + private static int[] B1_IDENT1 = { 28,29,30,31,32,33,34,35,36,37,38,39,40 }; private static int[] B1_CONTROL_CHANNEL = { 31,32,33,34,35,36,37,38,39,40 }; + private static int[] B1_CHANNEL = { 35,36,37,38,39,40,41,42,43,44 }; + private static int[] B1_MESSAGE_TYPE = { 41,42,43,44,45,46,47,48,49 }; + private static int[] B1_GTC_CHAN = { 43,44,45,46,47,48,49,50,51,52 }; + private static int[] B1_PREFIX2 = { 48,49,50,51,52,53,54 }; + private static int[] B1_IDENT2 = { 50,51,52,53,54,55,56,57,58,59,60,61,62 }; + private static int[] B1_IDENT2_GTC = { 53,54,55,56,57,58,59,60,61,62,63,64,65 }; + private static int[] B1_IDENT2_HEAD = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; private static int[] B1_ADJSITE = { 49,50,51,52 }; + private static int B1_PERIODIC_CALL_MAINT_MESSAGES = 50; + private static int[] B1_MAINT_MESSAGE_INTERVAL = { 51,52,53,54,55 }; + private static int[] B1_WAIT_TIME = { 54,55,56 }; + private static int B1_PRESSEL_ON_REQUIRED = 56; + private static int[] B1_ALOHA_RSVD = { 57,58 }; + private static int[] B1_ALOHA_M = { 59,60,61,62,63 }; + private static int[] B1_ALOHA_N = { 64,65,66,67 }; + private static int B1_IDENT1_ID_VALUE = 57; private static int[] B1_SLOTS = { 63,64 }; private static int[] B1_DESCRIPTOR = { 65,66,67 }; + private static int[] B1_STATUS_MESSAGE = { 63,64,65,66,67 }; - - /* Block 2 Fields */ + /* Block 2 or First Data Codeword Fields */ private static int[] B2_SYSTEM_ID = { 85,86,87,88,89,90,91,92,93,94,95,96,97,98,99 }; private static int[] B2_PREFIX = { 112,113,114,115,116,117,118 }; private static int[] B2_IDENT2 = { 119,120,121,122,123,124,125,126,127,128,129,130,131 }; + + private static int B2_SDM_SEGMENT_TRANSACTION_FLAG = 85; + private static int[] B2_SDM_GENERAL_FORMAT = { 86,87,88 }; + private static int B2_SDM_INITIAL_SEGMENT_FLAG = 89; + private static int B2_SDM_STF0_START = 86; + private static int B2_SDM_STF0_END = 131; + private static int B2_SDM_STF1_START = 90; + private static int B2_SDM_STF1_END = 131; + + /* Block 3 or Second Data Codeword Fields */ + private static int B3_SDM_RETURN_SLOT_ACCESS = 149; + private static int B3_SDM_START = 150; + private static int B3_SDM_END = 195; + + /* Block 4 or Third Data Codeword Fields */ + private static int B4_SDM_SEGMENT_TRANSACTION_FLAG = 213; + private static int[] B4_SDM_NUMBER_SEGMENTS = { 214,215 }; + private static int B4_SDM_CONTINUATION_SEGMENT_FLAG = 216; + private static int B4_SDM_RESERVED_FLAG = 217; + private static int B4_SDM_STF0_START = 214; + private static int B4_SDM_STF0_END = 259; + private static int B4_SDM_STF1_START = 218; + private static int B4_SDM_STF1_END = 259; + + /* Block 5 or Fourth Data Codeword Fields */ + private static int B5_SDM_RETURN_SLOT_ACCESS = 277; + private static int B5_SDM_START = 278; + private static int B5_SDM_END = 323; + + private static DecimalFormat mFrequencyFormatter = new DecimalFormat("#.#####"); + private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" ); + Calendar mCalendar = new GregorianCalendar(); private BitSetBuffer mMessage; private CRC[] mCRC = new CRC[ 5 ]; @@ -92,6 +142,7 @@ public class MPT1327Message extends Message switch( mMessageType ) { /* 1 data block messages */ + case AHYC: case CLEAR: case MAINT: case MOVE: @@ -99,21 +150,27 @@ public class MPT1327Message extends Message /* 2 data block messages */ case AHYQ: case ALH: + case HEAD_PLUS1: checkParity( 1, BLOCK_2_START, BLOCK_3_START ); break; /* 3 data block messages */ + case HEAD_PLUS2: + checkParity( 1, BLOCK_2_START, BLOCK_3_START ); + checkParity( 2, BLOCK_3_START, BLOCK_4_START ); + break; /* 4 data block messages */ case ACKT: + case HEAD_PLUS3: checkParity( 1, BLOCK_2_START, BLOCK_3_START ); checkParity( 2, BLOCK_3_START, BLOCK_4_START ); checkParity( 3, BLOCK_4_START, BLOCK_5_START ); break; /* 5 data block messages */ - case AHYC: + case HEAD_PLUS4: checkParity( 1, BLOCK_2_START, BLOCK_3_START ); checkParity( 2, BLOCK_3_START, BLOCK_4_START ); checkParity( 3, BLOCK_4_START, BLOCK_5_START ); - checkParity( 4, BLOCK_5_START, BLOCK_5_END ); + checkParity( 4, BLOCK_5_START, BLOCK_6_START ); break; case ACK: case ACKB: @@ -134,9 +191,28 @@ public class MPT1327Message extends Message case BCAST: case GTC: case MARK: - break; - default: - break; + case DACKD: + case DACKZ: + case DACK_DAL: + case DACK_DALG: + case DACK_DALN: + case DACK_GO: + case DAHY: + case DAHYX: + case DAHYZ: + case DRQG: + case DRQX: + case DRQZ: + case GTT: + case RLA: + case SACK: + case SAMIS: + case SAMIU: + case SAMO: + case SITH: + case UNKN: + default: + break; } } else @@ -145,11 +221,20 @@ public class MPT1327Message extends Message } } + /** + * Performs CRC check against the specified section/block, using the + * message bits between start and end. + */ private void checkParity( int section, int start, int end ) { mCRC[ section ] = detectAndCorrect( start, end ); } + /** + * Performs CRC check and corrects some bit errors. + */ + + //TODO: move this to the CRC class private CRC detectAndCorrect( int start, int end ) { BitSet original = mMessage.get( start, end ); @@ -188,12 +273,18 @@ public class MPT1327Message extends Message return "[" + CRC.format( mCRC ) + "]"; } + /** + * Indicates if Block 1 of the message has passed the CRC check. + */ public boolean isValid() { return mCRC[ 0 ] == CRC.PASSED || mCRC[ 0 ] == CRC.CORRECTED; } - + + /** + * Constructs a decoded message string + */ @Override public String toString() { @@ -215,14 +306,20 @@ public class MPT1327Message extends Message return sb.toString(); } - + + /** + * Determines the message type from block 1 + */ public MPTMessageType getMessageType() { int type = mMessage.getInt( B1_MESSAGE_TYPE ); return MPTMessageType.fromNumber( type ); } - + + /** + * MPT1327 Site identifier + */ public int getSiteID() { if( mMessageType == MPTMessageType.BCAST ) @@ -239,6 +336,9 @@ public class MPT1327Message extends Message } } + /** + * Indicates if this message has a system identifier + */ public boolean hasSystemID() { return getSiteID() != 0; @@ -251,6 +351,27 @@ public class MPT1327Message extends Message return SystemDefinition.fromNumber( sysdef ); } + public boolean getPeriodicMaintenanceMessagesRequired() + { + return mMessage.get( B1_PERIODIC_CALL_MAINT_MESSAGES ); + } + + public int getMaintenanceMessageInterval() + { + return mMessage.getInt( B1_MAINT_MESSAGE_INTERVAL ); + } + + public boolean getPresselOnRequired() + { + return mMessage.get( B1_PRESSEL_ON_REQUIRED ); + } + + public String getMaintMessageIDENT1Value() + { + return ( mMessage.get( B1_IDENT1_ID_VALUE ) ? "GROUP ADDRESS" : + "INDIVIDUAL ADDRESS" ); + } + public int getChannel() { switch( mMessageType ) @@ -303,10 +424,19 @@ public class MPT1327Message extends Message public int getIdent2() { - if( getMessageType() == MPTMessageType.GTC ) + MPTMessageType type = getMessageType(); + + if( type == MPTMessageType.GTC ) { return mMessage.getInt( B1_IDENT2_GTC ); } + else if( type == MPTMessageType.HEAD_PLUS1 || + type == MPTMessageType.HEAD_PLUS2 || + type == MPTMessageType.HEAD_PLUS3 || + type == MPTMessageType.HEAD_PLUS4 ) + { + return mMessage.getInt( B1_IDENT2_HEAD ); + } else { return mMessage.getInt( B1_IDENT2 ); @@ -557,8 +687,20 @@ public class MPT1327Message extends Message sb.append( " ID:" ); sb.append( getToID() ); } + + sb.append( " WAIT:" ); + sb.append( getWaitTime() ); + sb.append( " RSVD:" ); + sb.append( getAlohaReserved() ); + sb.append( " M:" ); + sb.append( getAlohaM() ); + sb.append( " N:" ); + sb.append( getAlohaN() ); break; case BCAST: + sb.append( " SYSTEM:" ); + sb.append( format( getSiteID(), 5 ) ); + SystemDefinition sysdef = getSystemDefinition(); sb.append( " " ); @@ -579,6 +721,15 @@ public class MPT1327Message extends Message //debug sb.append( " *************************" ); break; + case SPECIFY_CALL_MAINTENANCE_PARAMETERS: + sb.append( " PERIODIC MAINT MSG REQD:" ); + sb.append( getPeriodicMaintenanceMessagesRequired() ); + sb.append( " INTERVAL:" ); + sb.append( getMaintenanceMessageInterval() ); + sb.append( "(sec) PRESSEL ON REQD:" ); + sb.append( getPresselOnRequired() ); + sb.append( " IDENT1 VALUE:" ); + sb.append( getMaintMessageIDENT1Value() ); } break; case CLEAR: @@ -610,6 +761,13 @@ public class MPT1327Message extends Message sb.append( getToID() ); } + break; + case HEAD_PLUS1: + case HEAD_PLUS2: + case HEAD_PLUS3: + case HEAD_PLUS4: + sb.append( "SHORT DATA MESSAGE: " ); + sb.append( mMessageType.getDescription() ); break; } @@ -694,7 +852,10 @@ public class MPT1327Message extends Message return sb.toString(); } - + + /** + * Constructs a FROM identifier from the Prefix2 and Ident2 values + */ @Override public String getFromID() { @@ -723,15 +884,23 @@ public class MPT1327Message extends Message public boolean hasFromID() { - return getFromID() != null; + return getFromID() != null && !getFromID().isEmpty(); } @Override public Alias getFromIDAlias() { + if( mAliasList != null && hasFromID() ) + { + return mAliasList.getMPT1327Alias( getFromID() ); + } + return null; } + /** + * Constructs a TO identifier from the Prefix1 and Ident1 fields + */ @Override public String getToID() { @@ -742,11 +911,6 @@ public class MPT1327Message extends Message switch( IdentType.fromIdent( ident ) ) { - case USER: - sb.append( format( prefix, 3 ) ); - sb.append( "-" ); - sb.append( format( ident, 4) ); - break; case IPFIXI: sb.append( "INTER-PREFIX" ); break; @@ -782,6 +946,12 @@ public class MPT1327Message extends Message case DIVERTI: sb.append( "CALL DIVERT" ); break; + case USER: + default: + sb.append( format( prefix, 3 ) ); + sb.append( "-" ); + sb.append( format( ident, 4) ); + break; } return sb.toString(); @@ -789,14 +959,22 @@ public class MPT1327Message extends Message public boolean hasToID() { - return getToID() != null; + return getToID() != null && !getToID().isEmpty(); } - + + /** + * Data message codeword descriptor. Indicates the type of data message + * that the radio unit shall respond with. + */ public Descriptor getDescriptor() { return Descriptor.fromNumber( mMessage.getInt( B1_DESCRIPTOR ) ); } + /** + * Indicates the number of data codeword slots that are appended to a HEAD + * message + */ public Slots getSlots() { return Slots.fromNumber( mMessage.getInt( B1_SLOTS ) ); @@ -814,19 +992,589 @@ public class MPT1327Message extends Message return null; } } + + /** + * Aloha wait time - random access protocol + */ + public int getWaitTime() + { + return mMessage.getInt( B1_WAIT_TIME ); + } + /** + * Aloha reserved field + */ + public int getAlohaReserved() + { + return mMessage.getInt( B1_ALOHA_RSVD ); + } + + /** + * Aloha M field + */ + public int getAlohaM() + { + return mMessage.getInt( B1_ALOHA_M ); + } + + /** + * Aloha N field + */ + public int getAlohaN() + { + return mMessage.getInt( B1_ALOHA_N ); + } + + /** + * String representation of the CRC check for each 64-bit message block in + * this message. + */ @Override public String getErrorStatus() { return getParity(); } + /** + * Used by the map to get a plottable version of this event + */ @Override public Plottable getPlottable() { return null; } + /** + * Indicates if the received SDM is formatted according to the MPT-1327 or + * the MPT-1343 ICD. + */ + public SDMFormat getSDMFormat() + { + return SDMFormat.valueOf( mMessage.get( B2_SDM_SEGMENT_TRANSACTION_FLAG ), + mMessage.getInt( B2_SDM_GENERAL_FORMAT ) ); + } + + public String getSDM() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "SDM:" ); + + MPTMessageType type = getMessageType(); + + switch( type ) + { + case HEAD_PLUS1: + case HEAD_PLUS2: + case HEAD_PLUS3: + case HEAD_PLUS4: + SDMFormat format = getSDMFormat(); + + switch( format ) + { + case MPT1327: + sb.append( "MPT1327 " ); + sb.append( getSDMBinary( type, format ) ); + break; + case MPT1343_BINARY: + sb.append( "BINARY " ); + sb.append( getSDMBinary( type, format ) ); + break; + case MPT1343_COMMAND: + sb.append( "COMMAND " ); + sb.append( getSDMBinary( type, format ) ); + break; + case MPT1343_MAP27: + sb.append( "MAP27 " ); + sb.append( getSDMBinary( type, format ) ); + break; + case MPT1343_RESERVED: + sb.append( "RESERVED " ); + sb.append( getSDMBinary( type, format ) ); + break; + case MPT1343_SPARE: + sb.append( "SPARE " ); + sb.append( getSDMBinary( type, format ) ); + break; + case MPT1343_ASCII: + sb.append( "ASCII " ); + sb.append( getSDMASCII( type ) ); + break; + case MPT1343_BCD: + sb.append( "BCD " ); + sb.append( getSDMBCD( type ) ); + break; + case MPT1343_TELEX: + sb.append( "TELEX " ); + sb.append( getSDMTelex( type ) ); + break; + case UNKNOWN: + default: + sb.append( "UNKNOWN " ); + break; + } + + break; + case SAMO: + sb.append( "SAMO " ); + break; + case SAMIS: + sb.append( "SAMIS " ); + break; + case SAMIU: + sb.append( "SAMIU " ); + break; + default: + break; + + } + + return sb.toString(); + } + + public String getSDMBinary( MPTMessageType type, SDMFormat format ) + { + StringBuilder sb = new StringBuilder(); + + switch( type ) + { + case HEAD_PLUS1: + if( format == SDMFormat.MPT1327 ) + { + sb.append( mMessage.getHex( B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12 ) ); + } + else + { + sb.append( mMessage.getHex( B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12 ) ); + } + break; + case HEAD_PLUS2: + if( format == SDMFormat.MPT1327 ) + { + sb.append( mMessage.getHex( B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B3_SDM_START, + B3_SDM_END, + 12 ) ); + } + else + { + sb.append( mMessage.getHex( B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B3_SDM_START, + B3_SDM_END, + 12 ) ); + } + break; + case HEAD_PLUS3: + if( format == SDMFormat.MPT1327 ) + { + sb.append( mMessage.getHex( B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B3_SDM_START, + B3_SDM_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B4_SDM_STF0_START, + B4_SDM_STF0_END, + 12 ) ); + } + else + { + sb.append( mMessage.getHex( B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B3_SDM_START, + B3_SDM_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B4_SDM_STF1_START, + B4_SDM_STF1_END, + 12 ) ); + } + break; + case HEAD_PLUS4: + if( format == SDMFormat.MPT1327 ) + { + sb.append( mMessage.getHex( B2_SDM_STF0_START, + B2_SDM_STF0_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B3_SDM_START, + B3_SDM_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B4_SDM_STF0_START, + B4_SDM_STF0_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B5_SDM_START, + B5_SDM_END, + 12 ) ); + } + else + { + sb.append( mMessage.getHex( B2_SDM_STF1_START, + B2_SDM_STF1_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B3_SDM_START, + B3_SDM_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B4_SDM_STF1_START, + B4_SDM_STF1_END, + 12 ) ); + sb.append( " " ); + sb.append( mMessage.getHex( B5_SDM_START, + B5_SDM_END, + 12 ) ); + } + break; + } + + return sb.toString(); + } + + public String getSDMASCII( MPTMessageType type ) + { + StringBuilder sb = new StringBuilder(); + + int blocks = 0; + + switch( type ) + { + case HEAD_PLUS1: + blocks = 1; + break; + case HEAD_PLUS2: + blocks = 2; + break; + case HEAD_PLUS3: + blocks = 3; + break; + case HEAD_PLUS4: + blocks = 4; + break; + } + + if( blocks >= 1 ) + { + sb.append( (char)mMessage.getInt( 91, 97 ) ); + sb.append( (char)mMessage.getInt( 98, 104 ) ); + sb.append( (char)mMessage.getInt( 105, 111 ) ); + sb.append( (char)mMessage.getInt( 112, 118 ) ); + sb.append( (char)mMessage.getInt( 119, 125 ) ); + } + + if( blocks >= 2 ) + { + sb.append( (char)mMessage.getInt( + new int[] { 126,127,128,129,130,131,150 } ) ); + sb.append( (char)mMessage.getInt( 151, 157 ) ); + sb.append( (char)mMessage.getInt( 158, 164 ) ); + sb.append( (char)mMessage.getInt( 165, 171 ) ); + sb.append( (char)mMessage.getInt( 172, 178 ) ); + sb.append( (char)mMessage.getInt( 179, 185 ) ); + sb.append( (char)mMessage.getInt( 186, 192 ) ); + } + + if( blocks >= 3 ) + { + sb.append( (char)mMessage.getInt( + new int[] { 193,194,195,218,219,220,221 } ) ); + sb.append( (char)mMessage.getInt( 222, 228 ) ); + sb.append( (char)mMessage.getInt( 229, 235 ) ); + sb.append( (char)mMessage.getInt( 236, 242 ) ); + sb.append( (char)mMessage.getInt( 243, 249 ) ); + sb.append( (char)mMessage.getInt( 250, 256 ) ); + } + + if( blocks == 4 ) + { + sb.append( (char)mMessage.getInt( + new int[] { 257,258,259,278,279,280,281 } ) ); + sb.append( (char)mMessage.getInt( 282, 288 ) ); + sb.append( (char)mMessage.getInt( 289, 295 ) ); + sb.append( (char)mMessage.getInt( 296, 302 ) ); + sb.append( (char)mMessage.getInt( 303, 309 ) ); + sb.append( (char)mMessage.getInt( 310, 316 ) ); + sb.append( (char)mMessage.getInt( 317, 323 ) ); + } + + return sb.toString(); + } + + public String getSDMBCD( MPTMessageType type ) + { + StringBuilder sb = new StringBuilder(); + + int blocks = 0; + + switch( type ) + { + case HEAD_PLUS1: + blocks = 1; + break; + case HEAD_PLUS2: + blocks = 2; + break; + case HEAD_PLUS3: + blocks = 3; + break; + case HEAD_PLUS4: + blocks = 4; + break; + } + + if( blocks >= 1 ) + { + sb.append( getBCD( mMessage.getInt( 90, 93 ) ) ); + sb.append( getBCD( mMessage.getInt( 94, 97 ) ) ); + sb.append( getBCD( mMessage.getInt( 98, 101 ) ) ); + sb.append( getBCD( mMessage.getInt( 102, 105 ) ) ); + sb.append( getBCD( mMessage.getInt( 106, 109 ) ) ); + sb.append( getBCD( mMessage.getInt( 110, 113 ) ) ); + sb.append( getBCD( mMessage.getInt( 114, 117 ) ) ); + sb.append( getBCD( mMessage.getInt( 118, 121 ) ) ); + sb.append( getBCD( mMessage.getInt( 122, 125 ) ) ); + sb.append( getBCD( mMessage.getInt( 126, 129 ) ) ); + } + + if( blocks >= 2 ) + { + sb.append( getBCD( mMessage.getInt( + new int[] { 130,131,150,151 } ) ) ); + sb.append( getBCD( mMessage.getInt( 152, 155 ) ) ); + sb.append( getBCD( mMessage.getInt( 156, 159 ) ) ); + sb.append( getBCD( mMessage.getInt( 160, 163 ) ) ); + sb.append( getBCD( mMessage.getInt( 164, 167 ) ) ); + sb.append( getBCD( mMessage.getInt( 168, 171 ) ) ); + sb.append( getBCD( mMessage.getInt( 172, 175 ) ) ); + sb.append( getBCD( mMessage.getInt( 176, 179 ) ) ); + sb.append( getBCD( mMessage.getInt( 180, 183 ) ) ); + sb.append( getBCD( mMessage.getInt( 184, 187 ) ) ); + sb.append( getBCD( mMessage.getInt( 188, 191 ) ) ); + sb.append( getBCD( mMessage.getInt( 192, 195 ) ) ); + } + + if( blocks >= 3 ) + { + sb.append( getBCD( mMessage.getInt( 218, 221 ) ) ); + sb.append( getBCD( mMessage.getInt( 222, 225 ) ) ); + sb.append( getBCD( mMessage.getInt( 226, 229 ) ) ); + sb.append( getBCD( mMessage.getInt( 230, 233 ) ) ); + sb.append( getBCD( mMessage.getInt( 234, 237 ) ) ); + sb.append( getBCD( mMessage.getInt( 238, 241 ) ) ); + sb.append( getBCD( mMessage.getInt( 242, 245 ) ) ); + sb.append( getBCD( mMessage.getInt( 246, 249 ) ) ); + sb.append( getBCD( mMessage.getInt( 250, 253 ) ) ); + sb.append( getBCD( mMessage.getInt( 254, 257 ) ) ); + } + + if( blocks == 4 ) + { + sb.append( getBCD( mMessage.getInt( + new int[] { 258,259,278,279 } ) ) ); + sb.append( getBCD( mMessage.getInt( 280, 283 ) ) ); + sb.append( getBCD( mMessage.getInt( 284, 287 ) ) ); + sb.append( getBCD( mMessage.getInt( 288, 291 ) ) ); + sb.append( getBCD( mMessage.getInt( 292, 295 ) ) ); + sb.append( getBCD( mMessage.getInt( 296, 299 ) ) ); + sb.append( getBCD( mMessage.getInt( 300, 303 ) ) ); + sb.append( getBCD( mMessage.getInt( 304, 307 ) ) ); + sb.append( getBCD( mMessage.getInt( 308, 311 ) ) ); + sb.append( getBCD( mMessage.getInt( 312, 315 ) ) ); + sb.append( getBCD( mMessage.getInt( 316, 319 ) ) ); + sb.append( getBCD( mMessage.getInt( 320, 232 ) ) ); + } + + return sb.toString(); + } + + private String getBCD( int value ) + { + if( 0 <= value && value <= 12 ) + { + return BCD[ value ]; + } + else + { + return "?"; + } + } + + public String getSDMTelex( MPTMessageType type ) + { + ArrayList values = new ArrayList(); + + int blocks = 0; + + switch( type ) + { + case HEAD_PLUS1: + blocks = 1; + break; + case HEAD_PLUS2: + blocks = 2; + break; + case HEAD_PLUS3: + blocks = 3; + break; + case HEAD_PLUS4: + blocks = 4; + break; + } + + if( blocks >= 1 ) + { + values.add( mMessage.getInt( 91, 95 ) ); + values.add( mMessage.getInt( 96, 100 ) ); + values.add( mMessage.getInt( 101, 105 ) ); + values.add( mMessage.getInt( 106, 110 ) ); + values.add( mMessage.getInt( 111, 115 ) ); + values.add( mMessage.getInt( 116, 120 ) ); + values.add( mMessage.getInt( 121, 125 ) ); + values.add( mMessage.getInt( 126, 130 ) ); + } + + if( blocks >= 2 ) + { + values.add( mMessage.getInt( + new int[] { 131,150,151,152,153 } ) ); + values.add( mMessage.getInt( 154, 158 ) ); + values.add( mMessage.getInt( 159, 163 ) ); + values.add( mMessage.getInt( 164, 168 ) ); + values.add( mMessage.getInt( 169, 173 ) ); + values.add( mMessage.getInt( 174, 178 ) ); + values.add( mMessage.getInt( 179, 183 ) ); + values.add( mMessage.getInt( 184, 188 ) ); + values.add( mMessage.getInt( 189, 193 ) ); + } + + if( blocks >= 3 ) + { + values.add( mMessage.getInt( + new int[] { 194,195,218,219,220 } ) ); + values.add( mMessage.getInt( 221, 225 ) ); + values.add( mMessage.getInt( 226, 230 ) ); + values.add( mMessage.getInt( 231, 235 ) ); + values.add( mMessage.getInt( 236, 240 ) ); + values.add( mMessage.getInt( 241, 245 ) ); + values.add( mMessage.getInt( 246, 250 ) ); + } + + if( blocks == 4 ) + { + values.add( mMessage.getInt( + new int[] { 251,252,253,254,278 } ) ); + values.add( mMessage.getInt( 279, 283 ) ); + values.add( mMessage.getInt( 284, 288 ) ); + values.add( mMessage.getInt( 289, 293 ) ); + values.add( mMessage.getInt( 294, 298 ) ); + values.add( mMessage.getInt( 299, 303 ) ); + values.add( mMessage.getInt( 304, 308 ) ); + values.add( mMessage.getInt( 309, 313 ) ); + values.add( mMessage.getInt( 314, 318 ) ); + values.add( mMessage.getInt( 319, 323 ) ); + } + + StringBuilder sb = new StringBuilder(); + + /* Decode the numeric values. 28 and 29 are used to shift the decoder + * to use the figures alphabet or the letters alphabet */ + boolean figure = false; + + for( Integer value: values ) + { + if( 0 <= value && value < 32 ) + { + if( value == 28 ) + { + figure = false; + } + else if( value == 29 ) + { + figure = true; + } + else + { + if( figure ) + { + sb.append( TELEX_FIGURES[ value ] ); + } + else + { + sb.append( TELEX_LETTERS[ value ] ); + } + } + } + } + + return sb.toString(); + } + + /** + * SDM Message format ICDs + */ + public enum SDMFormat + { + MPT1327, + MPT1343_BINARY, + MPT1343_BCD, + MPT1343_TELEX, + MPT1343_ASCII, + MPT1343_RESERVED, + MPT1343_SPARE, + MPT1343_COMMAND, + MPT1343_MAP27, + UNKNOWN; + + public static SDMFormat valueOf( boolean stf, int gfi ) + { + if( stf ) //MPT1343 + { + switch( gfi ) + { + case 0: + return MPT1343_BINARY; + case 1: + return MPT1343_BCD; + case 2: + return MPT1343_TELEX; + case 3: + return MPT1343_ASCII; + case 4: + return MPT1343_RESERVED; + case 5: + return MPT1343_SPARE; + case 6: + return MPT1343_COMMAND; + case 7: + return MPT1343_MAP27; + default: + return UNKNOWN; + } + } + else + { + return MPT1327; + } + } + } + + public enum IdentType { ALLI( "SYSTEM-WIDE" ), @@ -953,53 +1701,76 @@ public class MPT1327Message extends Message public enum Descriptor { - DESC_0( "EXTENDED ADDRESSING INFORMATION", "SERIAL NUMBER" ), - DESC_1( "PSTN DIALED DIGITS", "RESERVED" ), - DESC_2( "PABX EXTENSION", "RESERVED" ), - RESERVED( "RESERVED", "RESERVED" ), - UNKNOWN( "UNKNOWN", "UNKNOWN" ); + DESC0( "EXTENDED ADDRESSING INFORMATION", + "SERIAL NUMBER", + "SINGLE SEGMENT TRANSACTIONS (SST) ONLY" ), + DESC1( "PSTN DIALED DIGITS", "RESERVED", "N/A" ), + DESC2( "PABX EXTENSION", "RESERVED", "N/A" ), + DESC3( "N/A", "N/A", "N/A" ), + DESC4( "N/A", "N/A", "FIRST MST SEGMENT OR SINGLE SST" ), + DESC5( "N/A", "N/A", "SECOND MST SEGMENT OR SINGLE SST" ), + DESC6( "N/A", "N/A", "THIRD MST SEGMENT OR SINGLE SST" ), + DESC7( "RESERVED", "RESERVED", "FOURTH MST SEGMENT OR SINGLE SST" ), + UNKNOWN( "UNKNOWN", "UNKNOWN", "UNKNOWN" ); private String mMode1Label; private String mMode2Label; + private String mSDMLabel; - private Descriptor( String mode1Label, String mode2Label ) + private Descriptor( String mode1Label, String mode2Label, String sdmLabel ) { mMode1Label = mode1Label; mMode2Label = mode2Label; + mSDMLabel = sdmLabel; } + /* Label that applies to AHYC Mode 1 responses */ public String getMode1Label() { return mMode1Label; } - + + /* Label that applies to AHYC Mode 2 responses */ public String getMode2Label() { return mMode2Label; } + + /* Label that applies to Short Data Messages (SDM) */ + public String getSDMLabel() + { + return mSDMLabel; + } public static Descriptor fromNumber( int number ) { switch( number ) { case 0: - return DESC_0; + return DESC0; case 1: - return DESC_1; + return DESC1; case 2: - return DESC_2; + return DESC2; case 3: + return DESC3; case 4: + return DESC4; case 5: + return DESC5; case 6: + return DESC6; case 7: - return RESERVED; + return DESC7; default: return UNKNOWN; } } } + /** + * Indicates the number of data codeword slots appended to a HEAD message + */ public enum Slots { SLOTS_0( "RESERVED" ), @@ -1085,53 +1856,66 @@ public class MPT1327Message extends Message public enum MPTMessageType { - UNKN( -1, "Unknown" ), - GTC( 0, "GTC - Goto Channel" ), - ALH( 256, "ALH - Aloha" ), - ALHS( 257, "ALHS - Standard Data Excluded" ), - ALHD( 258, "ALHD - Simple Calls Excluded" ), - ALHE( 259, "ALHE - Emergency Calls Only" ), - ALHR( 260, "ALHR - Emergency or Registration" ), - ALHX( 261, "ALHX - Registration Excluded" ), - ALHF( 262, "ALHF - Fallback Mode" ), + UNKN( "Unknown" ), + GTC( "GTC - Goto Channel" ), //0 + ALH( "ALH - Aloha" ), //256 + ALHS( "ALHS - Standard Data Excluded" ), //257 + ALHD( "ALHD - Simple Calls Excluded" ), //258 + ALHE( "ALHE - Emergency Calls Only" ), //259 + ALHR( "ALHR - Emergency or Registration" ), //260 + ALHX( "ALHX - Registration Excluded" ), //261 + ALHF( "ALHF - Fallback Mode" ), //262 + //263 + ACK( "ACK - Acknowledge" ), //264 + ACKI( "ACKI - More To Follow" ), //265 + ACKQ( "ACKQ - Call Queued" ), //266 + ACKX( "ACKX - Message Rejected" ), //267 + ACKV( "ACKV - Called Unit Unavailable" ), //268 + ACKE( "ACKE - Emergency" ), //269 + ACKT( "ACKT - Try On Given Address" ), //270 + ACKB( "ACKB - Call Back/Negative Ack" ), //271 + AHOY( "AHOY - General Availability Check" ), //272 + AHYX( "AHYX - Cancel Alert/Waiting Status" ), //274 + AHYP( "AHYP - Called Unit Presence Monitoring" ),//277 + AHYQ( "AHYQ - Status Message" ), //278 + AHYC( "AHYC - Short Data Message" ), //279 + MARK( "MARK - Control Channel Marker" ), //280 + MAINT( "MAINT - Call Maintenance Message"), //281 + CLEAR( "CLEAR - Down From Allocated Channel" ), //282 + MOVE( "MOVE - To Specified Channel" ), //283 + BCAST( "BCAST - System Parameters" ), //284 + //285 - 287 + SAMO( "SAMO - Outbound Single Address" ), //288 - 303 + SAMIS( "SAMIS - Inbound Solicited Single Address" ), //288 - 295 + SAMIU( "SAMIU - Inbound Unsolicited Single Address" ), //296 - 303 + HEAD_PLUS1( "HEAD - 1 DATA CODEWORD" ), //304 - 307 + HEAD_PLUS2( "HEAD - 2 DATA CODEWORDS" ), //308 - 311 + HEAD_PLUS3( "HEAD - 3 DATA CODEWORDS" ), //312 - 315 + HEAD_PLUS4( "HEAD - 4 DATA CODEWORDS" ), //316 - 319 + GTT( "GTT - Go To Transaction" ), //320 - 335 + SACK( "SACK - Standard Data Selective Ack Header" ), //416 - 423 + DACK_DAL( "DACK - Data Ack + DAL" ), //416 + DACK_DALG( "DACK - Data Ack + DALG" ), //417 + DACK_DALN( "DACK - Data Ack + DALN" ), //418 + DACK_GO( "DACK - GO Fragment Transmit Invitation" ), //419 + DACKZ( "DACKZ - Data Ack For Expedited Data"), //420 + DACKD( "DACKD - Data Ack For Standard Data" ), //421 + DAHY( "DAHY - Standard Data Ahoy" ), //424 + DRQG( "DRQG - Repeat Group Message" ), //426 + DRQZ( "DRQZ - Request Containing Expedited Data" ), //428 + DAHYZ( "DAHYZ - Expedited Data Ahoy" ), //428 + DAHYX( "DAHYX - Standard Data For Closing TRANS" ),//430 + DRQX( "DRQX - Request To Close A Transaction" ), //430 + RLA( "RLA - Repeat Last ACK" ), //431 + SITH( "SITH - Standard Data Address Codeword Data Item" ); //440 - 443 - ACK( 264, "ACK - Acknowledge" ), - ACKI( 265, "ACKI - More To Follow" ), - ACKQ( 266, "ACKQ - Call Queued" ), - ACKX( 267, "ACKX - Message Rejected" ), - ACKV( 268, "ACKV - Called Unit Unavailable" ), - ACKE( 269, "ACKE - Emergency" ), - ACKT( 270, "ACKT - Try On Given Address" ), - ACKB( 271, "ACKB - Call Back/Negative Ack" ), - - AHOY( 272, "AHOY - General Availability Check" ), - AHYX( 274, "AHYX - Cancel Alert/Waiting Status" ), - AHYP( 277, "AHYP - Called Unit Presence Monitoring" ), - AHYQ( 278, "AHYQ - Status Message" ), - AHYC( 279, "AHYC - Short Data Message" ), - - MARK( 280, "MARK - Control Channel Marker" ), - MAINT(281, "MAINT - Call Maintenance Message"), - CLEAR(282, "CLEAR - Down From Allocated Channel" ), - MOVE( 283, "MOVE - To Specified Channel" ), - BCAST(284, "BCAST - System Parameters" ); - - /* There are more to fill in here */ - - private int mNumber; private String mDescription; - private MPTMessageType( int number, String description ) + private MPTMessageType( String description ) { - mNumber = number; mDescription = description; } - public int getMessageNumber() - { - return mNumber; - } - public String getDescription() { return mDescription; @@ -1196,6 +1980,85 @@ public class MPT1327Message extends Message return MOVE; case 284: return BCAST; + case 288: + case 289: + case 290: + case 291: + case 292: + case 293: + case 294: + case 295: + case 296: + case 297: + case 298: + case 299: + case 300: + case 301: + case 302: + case 303: + return SAMO; + case 304: + case 305: + case 306: + case 307: + return HEAD_PLUS1; + case 308: + case 309: + case 310: + case 311: + return HEAD_PLUS2; + case 312: + case 313: + case 314: + case 315: + return HEAD_PLUS3; + case 316: + case 317: + case 318: + case 319: + return HEAD_PLUS4; + case 320: + case 321: + case 322: + case 323: + case 324: + case 325: + case 326: + case 327: + case 328: + case 329: + case 330: + case 331: + case 332: + case 333: + case 334: + case 335: + return GTT; + case 416: + return DACK_DAL; + case 417: + return DACK_DALG; + case 418: + return DACK_DALN; + case 419: + return DACK_GO; + case 420: + return DACKZ; + case 421: + return DACKD; + case 424: + return DAHY; + case 426: + return DRQG; + case 428: + return DAHYZ; + case 430: + return DAHYX; + case 440: + case 441: + case 442: + case 443: + return SITH; default: return UNKN; }